mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-25 20:01:50 +00:00
Bug 300410: Bugzilla::Auth needs to be restructured to not require a BEGIN block
Patch By Max Kanat-Alexander <mkanat@bugzilla.org> r=LpSolit, a=myk
This commit is contained in:
parent
9db2440e9e
commit
31d81c4061
webtools/bugzilla
Bugzilla.pm
Bugzilla
createaccount.cgiquery.cgirelogin.cgishow_bug.cgitemplate/en/default
token.cgiuserprefs.cgi@ -26,7 +26,7 @@ package Bugzilla;
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Auth;
|
||||
use Bugzilla::Auth::Login::WWW;
|
||||
use Bugzilla::Auth::Persist::Cookie;
|
||||
use Bugzilla::CGI;
|
||||
use Bugzilla::Config;
|
||||
use Bugzilla::Constants;
|
||||
@ -160,7 +160,13 @@ sub sudo_request {
|
||||
|
||||
sub login {
|
||||
my ($class, $type) = @_;
|
||||
my $authenticated_user = Bugzilla::Auth::Login::WWW->login($type);
|
||||
|
||||
my $authorizer = new Bugzilla::Auth();
|
||||
$type = LOGIN_REQUIRED if Bugzilla->cgi->param('GoAheadAndLogIn');
|
||||
if (!defined $type || $type == LOGIN_NORMAL) {
|
||||
$type = Param('requirelogin') ? LOGIN_REQUIRED : LOGIN_NORMAL;
|
||||
}
|
||||
my $authenticated_user = $authorizer->login($type);
|
||||
|
||||
# At this point, we now know if a real person is logged in.
|
||||
# We must now check to see if an sudo session is in progress.
|
||||
@ -200,14 +206,15 @@ sub logout {
|
||||
return unless user->id;
|
||||
|
||||
$option = LOGOUT_CURRENT unless defined $option;
|
||||
Bugzilla::Auth::Login::WWW->logout($_user, $option);
|
||||
Bugzilla::Auth::Persist::Cookie->logout({type => $option});
|
||||
Bugzilla->logout_request() unless $option eq LOGOUT_KEEP_CURRENT;
|
||||
}
|
||||
|
||||
sub logout_user {
|
||||
my ($class, $user) = @_;
|
||||
# When we're logging out another user we leave cookies alone, and
|
||||
# therefore avoid calling Bugzilla->logout() directly.
|
||||
Bugzilla::Auth::Login::WWW->logout($user, LOGOUT_ALL);
|
||||
Bugzilla::Auth::Persist::Cookie->logout({user => $user});
|
||||
}
|
||||
|
||||
# just a compatibility front-end to logout_user that gets a user by id
|
||||
|
@ -19,40 +19,181 @@
|
||||
#
|
||||
# Contributor(s): Bradley Baetz <bbaetz@acm.org>
|
||||
# Erik Stambaugh <erik@dasbistro.com>
|
||||
# Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
|
||||
package Bugzilla::Auth;
|
||||
|
||||
use strict;
|
||||
use fields qw(
|
||||
_info_getter
|
||||
_verifier
|
||||
_persister
|
||||
);
|
||||
|
||||
use Bugzilla::Config;
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::Config;
|
||||
use Bugzilla::Auth::Login::Stack;
|
||||
use Bugzilla::Auth::Verify::Stack;
|
||||
use Bugzilla::Auth::Persist::Cookie;
|
||||
|
||||
# The verification method that was successfully used upon login, if any
|
||||
my $current_verify_class = undef;
|
||||
use Switch;
|
||||
|
||||
# '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";
|
||||
}
|
||||
sub new {
|
||||
my ($class, $params) = @_;
|
||||
my $self = fields::new($class);
|
||||
|
||||
$params ||= {};
|
||||
$params->{Login} ||= Param('user_info_class') . ',Cookie';
|
||||
$params->{Verify} ||= Param('user_verify_class');
|
||||
|
||||
$self->{_info_getter} = new Bugzilla::Auth::Login::Stack($params->{Login});
|
||||
$self->{_verifier} = new Bugzilla::Auth::Verify::Stack($params->{Verify});
|
||||
# If we ever have any other login persistence methods besides cookies,
|
||||
# this could become more configurable.
|
||||
$self->{_persister} = new Bugzilla::Auth::Persist::Cookie();
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
# PRIVATE
|
||||
sub login {
|
||||
my ($self, $type) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
# 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;
|
||||
# Get login info from the cookie, form, environment variables, etc.
|
||||
my $login_info = $self->{_info_getter}->get_login_info();
|
||||
|
||||
if ($login_info->{failure}) {
|
||||
return $self->_handle_login_result($login_info, $type);
|
||||
}
|
||||
|
||||
# Now verify his username and password against the DB, LDAP, etc.
|
||||
if ($self->{_info_getter}->{successful}->requires_verification) {
|
||||
$login_info = $self->{_verifier}->check_credentials($login_info);
|
||||
if ($login_info->{failure}) {
|
||||
return $self->_handle_login_result($login_info, $type);
|
||||
}
|
||||
$login_info =
|
||||
$self->{_verifier}->{successful}->create_or_update_user($login_info);
|
||||
}
|
||||
else {
|
||||
$login_info = $self->{_verifier}->create_or_update_user($login_info);
|
||||
}
|
||||
|
||||
if ($login_info->{failure}) {
|
||||
return $self->_handle_login_result($login_info, $type);
|
||||
}
|
||||
|
||||
# Make sure the user isn't disabled.
|
||||
my $user = $login_info->{user};
|
||||
if ($user->disabledtext) {
|
||||
return $self->_handle_login_result({ failure => AUTH_DISABLED,
|
||||
user => $user }, $type);
|
||||
}
|
||||
$user->set_authorizer($self);
|
||||
|
||||
return $self->_handle_login_result($login_info, $type);
|
||||
}
|
||||
|
||||
sub can_change_password {
|
||||
my ($self) = @_;
|
||||
my $verifier = $self->{_verifier}->{successful};
|
||||
$verifier ||= $self->{_verifier};
|
||||
my $getter = $self->{_info_getter}->{successful};
|
||||
$getter = $self->{_info_getter}
|
||||
if (!$getter || $getter->isa('Bugzilla::Auth::Login::Cookie'));
|
||||
return $verifier->can_change_password &&
|
||||
$getter->user_can_create_account;
|
||||
}
|
||||
|
||||
sub can_login {
|
||||
my ($self) = @_;
|
||||
return $self->{_info_getter}->can_login;
|
||||
}
|
||||
|
||||
sub can_logout {
|
||||
my ($self) = @_;
|
||||
my $getter = $self->{_info_getter}->{successful};
|
||||
# If there's no successful getter, we're not logged in, so of
|
||||
# course we can't log out!
|
||||
return 0 unless $getter;
|
||||
return $getter->can_logout;
|
||||
}
|
||||
|
||||
sub user_can_create_account {
|
||||
my ($self) = @_;
|
||||
my $verifier = $self->{_verifier}->{successful};
|
||||
$verifier ||= $self->{_verifier};
|
||||
my $getter = $self->{_info_getter}->{successful};
|
||||
$getter = $self->{_info_getter}
|
||||
if (!$getter || $getter->isa('Bugzilla::Auth::Login::Cookie'));
|
||||
return $verifier->user_can_create_account
|
||||
&& $getter->user_can_create_account;
|
||||
}
|
||||
|
||||
sub can_change_email {
|
||||
return $_[0]->user_can_create_account;
|
||||
}
|
||||
|
||||
sub _handle_login_result {
|
||||
my ($self, $result, $login_type) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
my $user = $result->{user};
|
||||
my $fail_code = $result->{failure};
|
||||
|
||||
if (!$fail_code) {
|
||||
if ($self->{_info_getter}->{successful}->requires_persistence) {
|
||||
$self->{_persister}->persist_login($user);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
else {
|
||||
switch ($fail_code) {
|
||||
case AUTH_ERROR {
|
||||
ThrowCodeError($result->{error}, $result->{details});
|
||||
}
|
||||
case AUTH_NODATA {
|
||||
if ($login_type == LOGIN_REQUIRED) {
|
||||
# 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.
|
||||
$dbh->do("DELETE FROM logincookies WHERE " .
|
||||
$dbh->sql_to_days('NOW()') . " - " .
|
||||
$dbh->sql_to_days('lastused') . " > 30");
|
||||
$self->{_info_getter}->fail_nodata($self);
|
||||
}
|
||||
# Otherwise, we just return the "default" user.
|
||||
$user = Bugzilla->user;
|
||||
}
|
||||
|
||||
# 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)
|
||||
case [AUTH_LOGINFAILED, AUTH_NO_SUCH_USER] {
|
||||
ThrowUserError("invalid_username_or_password");
|
||||
}
|
||||
|
||||
# The account may be disabled
|
||||
case AUTH_DISABLED {
|
||||
$self->{_persister}->logout();
|
||||
# XXX This is NOT a good way to do this, architecturally.
|
||||
$self->{_persister}->clear_browser_cookies();
|
||||
# and throw a user error
|
||||
ThrowUserError("account_disabled",
|
||||
{'disabled_reason' => $result->{user}->disabledtext});
|
||||
}
|
||||
|
||||
# If we get here, then we've run out of options, which
|
||||
# shouldn't happen.
|
||||
else {
|
||||
ThrowCodeError("authres_unhandled",
|
||||
{ value => $fail_code });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
# Returns the network address for a given IP
|
||||
@ -72,84 +213,174 @@ sub get_netaddr {
|
||||
return "0.0.0.0" if ($maskbits == 0);
|
||||
|
||||
$addr >>= (32-$maskbits);
|
||||
|
||||
$addr <<= (32-$maskbits);
|
||||
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 = ();
|
||||
my $current_verify_method;
|
||||
for my $method (split /,\s*/, Param("user_verify_class")) {
|
||||
$current_verify_method = $method;
|
||||
$method = "Bugzilla::Auth::Verify::" . $method;
|
||||
@result = $method->authenticate(@args);
|
||||
@firstresult = @result unless @firstresult;
|
||||
|
||||
if (($result[0] != AUTH_NODATA)&&($result[0] != AUTH_LOGINFAILED)) {
|
||||
unshift @result, ($current_verify_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
|
||||
|
||||
my $chosen_verify_method;
|
||||
for my $method (split /,\s*/, Param("user_verify_class")) {
|
||||
$current_verify_method = $method;
|
||||
$method = "Bugzilla::Auth::Verify::" . $method;
|
||||
if ($method->can_edit('new')) {
|
||||
$chosen_verify_method = $method;
|
||||
}
|
||||
}
|
||||
|
||||
unshift @result, $chosen_verify_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__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Auth - Authentication handling for Bugzilla users
|
||||
Bugzilla::Auth - An object that authenticates the login credentials for
|
||||
a user.
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Handles authentication for Bugzilla users.
|
||||
|
||||
Authentication from Bugzilla involves two sets of modules. One set is
|
||||
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).
|
||||
used to obtain the username/password (from CGI, email, etc), and the
|
||||
other set uses this data to authenticate against the datasource
|
||||
(the Bugzilla DB, LDAP, PAM, etc.).
|
||||
|
||||
Modules for obtaining the data are located under L<Bugzilla::Auth::Login>, and
|
||||
modules for authenticating are located in L<Bugzilla::Auth::Verify>.
|
||||
Modules for obtaining the username/password are subclasses of
|
||||
L<Bugzilla::Auth::Login>, and modules for authenticating are subclasses
|
||||
of L<Bugzilla::Auth::Verify>.
|
||||
|
||||
=head1 AUTHENTICATION ERROR CODES
|
||||
|
||||
Whenever a method in the C<Bugzilla::Auth> family fails in some way,
|
||||
it will return a hashref containing at least a single key called C<failure>.
|
||||
C<failure> will point to an integer error code, and depending on the error
|
||||
code the hashref may contain more data.
|
||||
|
||||
The error codes are explained here below.
|
||||
|
||||
=head2 C<AUTH_NODATA>
|
||||
|
||||
Insufficient login data was provided by the user. This may happen in several
|
||||
cases, such as cookie authentication when the cookie is not present.
|
||||
|
||||
=head2 C<AUTH_ERROR>
|
||||
|
||||
An error occurred when trying to use the login mechanism.
|
||||
|
||||
The hashref will also contain an C<error> element, which is the name
|
||||
of an error from C<template/en/default/global/code-error.html> --
|
||||
the same type of error that would be thrown by
|
||||
L<Bugzilla::Error::ThrowCodeError>.
|
||||
|
||||
The hashref *may* contain an element called C<details>, which is a hashref
|
||||
that should be passed to L<Bugzilla::Error::ThrowCodeError> as the
|
||||
various fields to be used in the error message.
|
||||
|
||||
=head2 C<AUTH_LOGINFAILED>
|
||||
|
||||
An incorrect username or password was given.
|
||||
|
||||
=head2 C<AUTH_NO_SUCH_USER>
|
||||
|
||||
This is an optional more-specific version of C<AUTH_LOGINFAILED>.
|
||||
Modules should throw this error when they discover that the
|
||||
requested user account actually does not exist, according to them.
|
||||
|
||||
That is, for example, L<Bugzilla::Auth::Verify::LDAP> would throw
|
||||
this if the user didn't exist in LDAP.
|
||||
|
||||
The difference between C<AUTH_NO_SUCH_USER> and C<AUTH_LOGINFAILED>
|
||||
should never be communicated to the user, for security reasons.
|
||||
|
||||
=head2 C<AUTH_DISABLED>
|
||||
|
||||
The user successfully logged in, but their account has been disabled.
|
||||
Usually this is throw only by C<Bugzilla::Auth::login>.
|
||||
|
||||
=head1 LOGIN TYPES
|
||||
|
||||
The C<login> function (below) can do different types of login, depending
|
||||
on what constant you pass into it:
|
||||
|
||||
=head2 C<LOGIN_OPTIONAL>
|
||||
|
||||
A login is never required to access this data. Attempting to login is
|
||||
still useful, because this allows the page to be personalised. Note that
|
||||
an incorrect login will still trigger an error, even though the lack of
|
||||
a login will be OK.
|
||||
|
||||
=head2 C<LOGIN_NORMAL>
|
||||
|
||||
A login may or may not be required, depending on the setting of the
|
||||
I<requirelogin> parameter. This is the default if you don't specify a
|
||||
type.
|
||||
|
||||
=head2 C<LOGIN_REQUIRED>
|
||||
|
||||
A login is always required to access this data.
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
These are methods that can be called on a C<Bugzilla::Auth> object
|
||||
itself.
|
||||
|
||||
=head2 Login
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<login($type)>
|
||||
|
||||
Description: Logs a user in. For more details on how this works
|
||||
internally, see the section entitled "STRUCTURE."
|
||||
Params: $type - One of the Login Types from above.
|
||||
Returns: An authenticated C<Bugzilla::User>. Or, if the type was
|
||||
not C<LOGIN_REQUIRED>, then we return an
|
||||
empty C<Bugzilla::User> if no login data was passed in.
|
||||
|
||||
=back
|
||||
|
||||
=head2 Info Methods
|
||||
|
||||
These are methods that give information about the Bugzilla::Auth object.
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<can_change_password>
|
||||
|
||||
Description: Tells you whether or not the current login system allows
|
||||
changing passwords.
|
||||
Params: None
|
||||
Returns: C<true> if users and administrators should be allowed to
|
||||
change passwords, C<false> otherwise.
|
||||
|
||||
=item C<can_login>
|
||||
|
||||
Description: Tells you whether or not the current login system allows
|
||||
users to log in through the web interface.
|
||||
Params: None
|
||||
Returns: C<true> if users can log in through the web interface,
|
||||
C<false> otherwise.
|
||||
|
||||
=item C<can_logout>
|
||||
|
||||
Description: Tells you whether or not the current login system allows
|
||||
users to log themselves out.
|
||||
Params: None
|
||||
Returns: C<true> if users can log themselves out, C<false> otherwise.
|
||||
If a user isn't logged in, we always return C<false>.
|
||||
|
||||
=item C<user_can_create_account>
|
||||
|
||||
Description: Tells you whether or not users are allowed to manually create
|
||||
their own accounts, based on the current login system in use.
|
||||
Note that this doesn't check the C<createemailregexp>
|
||||
parameter--you have to do that by yourself in your code.
|
||||
Params: None
|
||||
Returns: C<true> if users are allowed to create new Bugzilla accounts,
|
||||
C<false> otherwise.
|
||||
|
||||
=item C<can_change_email>
|
||||
|
||||
Description: Whether or not the current login system allows users to
|
||||
change their own email address.
|
||||
Params: None
|
||||
Returns: C<true> if users can change their own email address,
|
||||
C<false> otherwise.
|
||||
|
||||
=back
|
||||
|
||||
=head1 CLASS FUNCTIONS
|
||||
|
||||
C<Bugzilla::Auth> contains several helper methods to be used by
|
||||
authentication or login modules.
|
||||
|
||||
@ -164,162 +395,118 @@ only some addresses.
|
||||
|
||||
=back
|
||||
|
||||
=head1 AUTHENTICATION
|
||||
=head1 STRUCTURE
|
||||
|
||||
Authentication modules check a user's credentials (username, password,
|
||||
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.
|
||||
This section is mostly interesting to developers who want to implement
|
||||
a new authentication type. It describes the general structure of the
|
||||
Bugzilla::Auth family, and how the C<login> function works.
|
||||
|
||||
=head2 METHODS
|
||||
A C<Bugzilla::Auth> object is essentially a collection of a few other
|
||||
objects: the "Info Getter," the "Verifier," and the "Persistence
|
||||
Mechanism."
|
||||
|
||||
They are used inside the C<login> function in the following order:
|
||||
|
||||
=head2 The Info Getter
|
||||
|
||||
This is a C<Bugzilla::Auth::Login> object. Basically, it gets the
|
||||
username and password from the user, somehow. Or, it just gets enough
|
||||
information to uniquely identify a user, and passes that on down the line.
|
||||
(For example, a C<user_id> is enough to uniquely identify a user,
|
||||
even without a username and password.)
|
||||
|
||||
Some Info Getters don't require any verification. For example, if we got
|
||||
the C<user_id> from a Cookie, we don't need to check the username and
|
||||
password.
|
||||
|
||||
If an Info Getter returns only a C<user_id> and no username/password,
|
||||
then it MUST NOT require verification. If an Info Getter requires
|
||||
verfication, then it MUST return at least a C<username>.
|
||||
|
||||
=head2 The Verifier
|
||||
|
||||
This verifies that the username and password are valid.
|
||||
|
||||
It's possible that some methods of verification don't require a password.
|
||||
|
||||
=head2 The Persistence Mechanism
|
||||
|
||||
This makes it so that the user doesn't have to log in on every page.
|
||||
Normally this object just sends a cookie to the user's web browser,
|
||||
as that's the most common method of "login persistence."
|
||||
|
||||
=head2 Other Things We Do
|
||||
|
||||
After we verify the username and password, sometimes we automatically
|
||||
create an account in the Bugzilla database, for certain authentication
|
||||
types. We use the "Account Source" to get data about the user, and
|
||||
create them in the database. (Or, if their data has changed since the
|
||||
last time they logged in, their data gets updated.)
|
||||
|
||||
=head2 The C<$login_data> Hash
|
||||
|
||||
All of the C<Bugzilla::Auth::Login> and C<Bugzilla::Auth::Verify>
|
||||
methods take an argument called C<$login_data>. This is basically
|
||||
a hash that becomes more and more populated as we go through the
|
||||
C<login> function.
|
||||
|
||||
All C<Bugzilla::Auth::Login> and C<Bugzilla::Auth::Verify> methods
|
||||
also *return* the C<$login_data> structure, when they succeed. They
|
||||
may have added new data to it.
|
||||
|
||||
For all C<Bugzilla::Auth::Login> and C<Bugzilla::Auth::Verify> methods,
|
||||
the rule is "you must return the same hashref you were passed in." You can
|
||||
modify the hashref all you want, but you can't create a new one. The only
|
||||
time you can return a new one is if you're returning some error code
|
||||
instead of the C<$login_data> structure.
|
||||
|
||||
Each C<Bugzilla::Auth::Login> or C<Bugzilla::Auth::Verify> method
|
||||
explains in its documentation which C<$login_data> elements are
|
||||
required by it, and which are set by it.
|
||||
|
||||
Here are all of the elements that *may* be in C<$login_data>:
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<authenticate($username, $pass)>
|
||||
=item C<user_id>
|
||||
|
||||
This method is passed a username and a password, and returns a list
|
||||
containing up to four return values, depending on the results of the
|
||||
authentication.
|
||||
A Bugzilla C<user_id> that uniquely identifies a user.
|
||||
|
||||
The first return value is the name of the class that generated the results
|
||||
constined in the remaining return values. The second return value is one of
|
||||
the status codes defined in L<Bugzilla::Constants|Bugzilla::Constants> and
|
||||
described below. The rest of the return values are status code-specific
|
||||
and are explained in the status code descriptions.
|
||||
=item C<username>
|
||||
|
||||
=item C<AUTH_OK>
|
||||
The username that was provided by the user.
|
||||
|
||||
Authentication succeeded. The third variable is the userid of the new
|
||||
user.
|
||||
=item C<bz_username>
|
||||
|
||||
=item C<AUTH_NODATA>
|
||||
The username of this user inside of Bugzilla. Sometimes this differs from
|
||||
C<username>.
|
||||
|
||||
Insufficient login data was provided by the user. This may happen in several
|
||||
cases, such as cookie authentication when the cookie is not present.
|
||||
=item C<password>
|
||||
|
||||
=item C<AUTH_ERROR>
|
||||
The password provided by the user.
|
||||
|
||||
An error occurred when trying to use the login mechanism. The third return
|
||||
value may contain the Bugzilla userid, but will probably be C<undef>,
|
||||
signifiying that the userid is unknown. The fourth value is a tag describing
|
||||
the error used by the authentication error templates to print a description
|
||||
to the user. The optional fifth argument is a hashref of values used as part
|
||||
of the tag's error descriptions.
|
||||
=item C<realname>
|
||||
|
||||
This error template must have a name/location of
|
||||
I<account/auth/C<lc(authentication-type)>-error.html.tmpl>.
|
||||
The real name of the user.
|
||||
|
||||
=item C<AUTH_LOGINFAILED>
|
||||
=item C<extern_id>
|
||||
|
||||
An incorrect username or password was given. Note that for security reasons,
|
||||
both cases return the same error code. However, in the case of a valid
|
||||
username, the third argument may be the userid. The authentication
|
||||
mechanism may not always be able to discover the userid if the password is
|
||||
not known, so whether or not this argument is present is implementation
|
||||
specific. For security reasons, the presence or lack of a userid value should
|
||||
not be communicated to the user.
|
||||
Some string that uniquely identifies the user in an external account
|
||||
source. If this C<extern_id> already exists in the database with
|
||||
a different username, the username will be *changed* to be the
|
||||
username specified in this C<$login_data>.
|
||||
|
||||
The fourth argument is an optional tag from the authentication server
|
||||
describing the error. The tag can be used by a template to inform the user
|
||||
about the error. Similar to C<AUTH_ERROR>, an optional hashref may be
|
||||
present as a fifth argument, to be used by the tag to give more detailed
|
||||
information.
|
||||
That is, let's my extern_id is C<mkanat>. I already have an account
|
||||
in Bugzilla with the username of C<mkanat@foo.com>. But this time,
|
||||
when I log in, I have an extern_id of C<mkanat> and a C<username>
|
||||
of C<mkanat@bar.org>. So now, Bugzilla will automatically change my
|
||||
username to C<mkanat@bar.org> instead of C<mkanat@foo.com>.
|
||||
|
||||
=item C<AUTH_DISABLED>
|
||||
=item C<user>
|
||||
|
||||
The user successfully logged in, but their account has been disabled.
|
||||
The third argument in the returned array is the userid, and the fourth
|
||||
is some text explaining why the account was disabled. This text would
|
||||
typically come from the C<disabledtext> field in the C<profiles> table.
|
||||
Note that this argument is a string, not a tag.
|
||||
|
||||
=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. 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.
|
||||
A L<Bugzilla::User> object representing the authenticated user.
|
||||
Note that C<Bugzilla::Auth::login> may modify this object at various points.
|
||||
|
||||
=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::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.
|
||||
|
||||
The login interface consists of the following methods:
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<login>, which takes a C<$type> argument, using constants found in
|
||||
C<Bugzilla::Constants>.
|
||||
|
||||
The login method may use various authentication modules (described
|
||||
above) to try to authenticate a user, and should return the userid on
|
||||
success, or C<undef> on failure.
|
||||
|
||||
When a login is required, but data is not present, it is the job of the
|
||||
login method to prompt the user for this data.
|
||||
|
||||
The constants accepted by C<login> include the following:
|
||||
|
||||
=item C<LOGIN_OPTIONAL>
|
||||
|
||||
A login is never required to access this data. Attempting to login is
|
||||
still useful, because this allows the page to be personalised. Note that
|
||||
an incorrect login will still trigger an error, even though the lack of
|
||||
a login will be OK.
|
||||
|
||||
=item C<LOGIN_NORMAL>
|
||||
|
||||
A login may or may not be required, depending on the setting of the
|
||||
I<requirelogin> parameter.
|
||||
|
||||
=item C<LOGIN_REQUIRED>
|
||||
|
||||
A login is always required to access this data.
|
||||
|
||||
=item C<logout>, which takes a C<Bugzilla::User> argument for the user
|
||||
being logged out, and an C<$option> argument. Possible values for
|
||||
C<$option> include:
|
||||
|
||||
=item C<LOGOUT_CURRENT>
|
||||
|
||||
Log out the user and invalidate his currently registered session.
|
||||
|
||||
=item C<LOGOUT_ALL>
|
||||
|
||||
Log out the user, and invalidate all sessions the user has registered in
|
||||
Bugzilla.
|
||||
|
||||
=item C<LOGOUT_KEEP_CURRENT>
|
||||
|
||||
Invalidate all sessions the user has registered excluding his current
|
||||
session; this option should leave the user logged in. This is useful for
|
||||
user-performed password changes.
|
||||
|
||||
=back
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<Bugzilla::Auth::Login::WWW::CGI>, L<Bugzilla::Auth::Login::WWW::CGI::Cookie>, L<Bugzilla::Auth::Verify::DB>
|
||||
|
||||
|
125
webtools/bugzilla/Bugzilla/Auth/Login.pm
Normal file
125
webtools/bugzilla/Bugzilla/Auth/Login.pm
Normal file
@ -0,0 +1,125 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# Contributor(s): Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
|
||||
package Bugzilla::Auth::Login;
|
||||
|
||||
use strict;
|
||||
use fields qw();
|
||||
|
||||
# Determines whether or not a user can logout. It's really a subroutine,
|
||||
# but we implement it here as a constant. Override it in subclasses if
|
||||
# that particular type of login method cannot log out.
|
||||
use constant can_logout => 1;
|
||||
use constant can_login => 1;
|
||||
use constant requires_persistence => 1;
|
||||
use constant requires_verification => 1;
|
||||
use constant user_can_create_account => 0;
|
||||
|
||||
sub new {
|
||||
my ($class) = @_;
|
||||
my $self = fields::new($class);
|
||||
return $self;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Auth::Login - Gets username/password data from the user.
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Bugzilla::Auth::Login is used to get information that uniquely identifies
|
||||
a user and allows us to authorize their Bugzilla access.
|
||||
|
||||
It is mostly an abstract class, requiring subclasses to implement
|
||||
most methods.
|
||||
|
||||
Note that callers outside of the C<Bugzilla::Auth> package should never
|
||||
create this object directly. Just create a C<Bugzilla::Auth> object
|
||||
and call C<login> on it.
|
||||
|
||||
=head1 LOGIN METHODS
|
||||
|
||||
These are methods that have to do with getting the actual login data
|
||||
from the user or handling a login somehow.
|
||||
|
||||
These methods are abstract -- they MUST be implemented by a subclass.
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<get_login_info()>
|
||||
|
||||
Description: Gets a username/password from the user, or some other
|
||||
information that uniquely identifies them.
|
||||
Params: None
|
||||
Returns: A C<$login_data> hashref. (See L<Bugzilla::Auth> for details.)
|
||||
The hashref MUST contain: C<user_id> *or* C<username>
|
||||
If this is a login method that requires verification,
|
||||
the hashref MUST contain C<password>.
|
||||
The hashref MAY contain C<realname> and C<extern_id>.
|
||||
|
||||
=item C<fail_nodata()>
|
||||
|
||||
Description: This function is called when Bugzilla doesn't get
|
||||
a username/password and the login type is C<LOGIN_REQUIRED>
|
||||
(See L<Bugzilla::Auth> for a description of C<LOGIN_REQUIRED>).
|
||||
That is, this handles C<AUTH_NODATA> in that situation.
|
||||
|
||||
This function MUST stop CGI execution when it is complete.
|
||||
That is, it must call C<exit> or C<ThrowUserError> or some
|
||||
such thing.
|
||||
Params: None
|
||||
Returns: Never Returns.
|
||||
|
||||
=back
|
||||
|
||||
=head1 INFO METHODS
|
||||
|
||||
These are methods that describe the capabilities of this
|
||||
C<Bugzilla::Auth::Login> object. These are all no-parameter
|
||||
methods that return either C<true> or C<false>.
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<can_logout>
|
||||
|
||||
Whether or not users can log out if they logged in using this
|
||||
object. Defaults to C<true>.
|
||||
|
||||
=item C<can_login>
|
||||
|
||||
Whether or not users can log in through the web interface using
|
||||
this object. Defaults to C<true>.
|
||||
|
||||
=item C<requires_persistence>
|
||||
|
||||
Whether or not we should send the user a cookie if they logged in with
|
||||
this method. Defaults to C<true>.
|
||||
|
||||
=item C<requires_verification>
|
||||
|
||||
Whether or not we should check the username/password that we
|
||||
got from this login method. Defaults to C<true>.
|
||||
|
||||
=item C<user_can_create_account>
|
||||
|
||||
Whether or not users can create accounts, if this login method is
|
||||
currently being used by the system. Defaults to C<false>.
|
||||
|
||||
=back
|
73
webtools/bugzilla/Bugzilla/Auth/Login/CGI.pm
Normal file
73
webtools/bugzilla/Bugzilla/Auth/Login/CGI.pm
Normal file
@ -0,0 +1,73 @@
|
||||
# -*- 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>
|
||||
# Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
|
||||
package Bugzilla::Auth::Login::CGI;
|
||||
use strict;
|
||||
use base qw(Bugzilla::Auth::Login);
|
||||
use constant user_can_create_account => 1;
|
||||
|
||||
use Bugzilla::Config;
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Util;
|
||||
use Bugzilla::User;
|
||||
|
||||
sub get_login_info {
|
||||
my ($self) = @_;
|
||||
my $cgi = Bugzilla->cgi;
|
||||
|
||||
my $username = trim($cgi->param("Bugzilla_login"));
|
||||
my $password = $cgi->param("Bugzilla_password");
|
||||
|
||||
$cgi->delete('Bugzilla_login', 'Bugzilla_password');
|
||||
|
||||
if (!defined $username || !defined $password) {
|
||||
return { failure => AUTH_NODATA };
|
||||
}
|
||||
|
||||
return { username => $username, password => $password };
|
||||
}
|
||||
|
||||
sub fail_nodata {
|
||||
my ($self) = @_;
|
||||
my $cgi = Bugzilla->cgi;
|
||||
my $template = Bugzilla->template;
|
||||
|
||||
# Redirect to SSL if required
|
||||
if (Param('sslbase') ne '' and Param('ssl') ne 'never') {
|
||||
$cgi->require_https(Param('sslbase'));
|
||||
}
|
||||
print $cgi->header();
|
||||
$template->process("account/auth/login.html.tmpl",
|
||||
{ 'target' => $cgi->url(-relative=>1) })
|
||||
|| ThrowTemplateError($template->error());
|
||||
exit;
|
||||
}
|
||||
|
||||
1;
|
83
webtools/bugzilla/Bugzilla/Auth/Login/Cookie.pm
Normal file
83
webtools/bugzilla/Bugzilla/Auth/Login/Cookie.pm
Normal file
@ -0,0 +1,83 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# Contributor(s): Bradley Baetz <bbaetz@acm.org>
|
||||
# Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
|
||||
package Bugzilla::Auth::Login::Cookie;
|
||||
use strict;
|
||||
use base qw(Bugzilla::Auth::Login);
|
||||
|
||||
use Bugzilla::Auth;
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::User;
|
||||
use Bugzilla::Util;
|
||||
|
||||
use constant requires_persistence => 0;
|
||||
use constant requires_verification => 0;
|
||||
use constant can_login => 0;
|
||||
|
||||
# Note that Cookie never consults the Verifier, it always assumes
|
||||
# it has a valid DB account or it fails.
|
||||
sub get_login_info {
|
||||
my ($self) = @_;
|
||||
my $cgi = Bugzilla->cgi;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
my $ip_addr = $cgi->remote_addr();
|
||||
my $net_addr = Bugzilla::Auth::get_netaddr($ip_addr);
|
||||
my $login_cookie = $cgi->cookie("Bugzilla_logincookie");
|
||||
my $user_id = $cgi->cookie("Bugzilla_login");
|
||||
|
||||
if ($login_cookie && $user_id) {
|
||||
# Anything goes for these params - they're just strings which
|
||||
# we're going to verify against the db
|
||||
trick_taint($ip_addr);
|
||||
trick_taint($login_cookie);
|
||||
detaint_natural($user_id);
|
||||
|
||||
my $query = "SELECT userid
|
||||
FROM logincookies
|
||||
WHERE logincookies.cookie = ?
|
||||
AND logincookies.userid = ?
|
||||
AND (logincookies.ipaddr = ?";
|
||||
|
||||
# If we have a network block that's allowed to use this cookie,
|
||||
# as opposed to just a single IP.
|
||||
my @params = ($login_cookie, $user_id, $ip_addr);
|
||||
if (defined $net_addr) {
|
||||
trick_taint($net_addr);
|
||||
$query .= " OR logincookies.ipaddr = ?";
|
||||
push(@params, $net_addr);
|
||||
}
|
||||
$query .= ")";
|
||||
|
||||
# If the cookie is valid, return a valid username.
|
||||
if ($dbh->selectrow_array($query, undef, @params)) {
|
||||
# If we logged in successfully, then update the lastused
|
||||
# time on the login cookie
|
||||
$dbh->do("UPDATE logincookies SET lastused = NOW()
|
||||
WHERE cookie = ?", undef, $login_cookie);
|
||||
return { user_id => $user_id };
|
||||
}
|
||||
}
|
||||
|
||||
# Either the he cookie is invalid, or we got no cookie. We don't want
|
||||
# to ever return AUTH_LOGINFAILED, because we don't want Bugzilla to
|
||||
# actually throw an error when it gets a bad cookie. It should just
|
||||
# look like there was no cokie to begin with.
|
||||
return { failure => AUTH_NODATA };
|
||||
}
|
||||
|
||||
1;
|
54
webtools/bugzilla/Bugzilla/Auth/Login/Env.pm
Normal file
54
webtools/bugzilla/Bugzilla/Auth/Login/Env.pm
Normal file
@ -0,0 +1,54 @@
|
||||
# -*- 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>
|
||||
# Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
|
||||
package Bugzilla::Auth::Login::Env;
|
||||
use strict;
|
||||
use base qw(Bugzilla::Auth::Login);
|
||||
|
||||
use Bugzilla::Config;
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::User;
|
||||
|
||||
use constant can_logout => 0;
|
||||
use constant can_login => 0;
|
||||
use constant requires_verification => 0;
|
||||
|
||||
sub get_login_info {
|
||||
my ($self) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
my $env_id = $ENV{Param("auth_env_id")} || '';
|
||||
my $env_email = $ENV{Param("auth_env_email")} || '';
|
||||
my $env_realname = $ENV{Param("auth_env_realname")} || '';
|
||||
|
||||
return { failure => AUTH_NODATA } if !$env_email;
|
||||
|
||||
return { username => $env_email, extern_id => $env_id,
|
||||
realname => $env_realname };
|
||||
}
|
||||
|
||||
sub fail_nodata {
|
||||
ThrowCodeError('env_no_email');
|
||||
}
|
||||
|
||||
1;
|
87
webtools/bugzilla/Bugzilla/Auth/Login/Stack.pm
Normal file
87
webtools/bugzilla/Bugzilla/Auth/Login/Stack.pm
Normal file
@ -0,0 +1,87 @@
|
||||
# -*- 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): Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
|
||||
package Bugzilla::Auth::Login::Stack;
|
||||
use strict;
|
||||
use base qw(Bugzilla::Auth::Login);
|
||||
use fields qw(
|
||||
_stack
|
||||
successful
|
||||
);
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $self = $class->SUPER::new(@_);
|
||||
my $list = shift;
|
||||
$self->{_stack} = [];
|
||||
foreach my $login_method (split(',', $list)) {
|
||||
require "Bugzilla/Auth/Login/${login_method}.pm";
|
||||
push(@{$self->{_stack}},
|
||||
"Bugzilla::Auth::Login::$login_method"->new(@_));
|
||||
}
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub get_login_info {
|
||||
my $self = shift;
|
||||
my $result;
|
||||
foreach my $object (@{$self->{_stack}}) {
|
||||
$result = $object->get_login_info(@_);
|
||||
$self->{successful} = $object;
|
||||
last if !$result->{failure};
|
||||
# So that if none of them succeed, it's undef.
|
||||
$self->{successful} = undef;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
sub fail_nodata {
|
||||
my $self = shift;
|
||||
# We fail from the bottom of the stack.
|
||||
my @reverse_stack = reverse @{$self->{_stack}};
|
||||
foreach my $object (@reverse_stack) {
|
||||
# We pick the first object that actually has the method
|
||||
# implemented.
|
||||
if ($object->can('fail_nodata')) {
|
||||
$object->fail_nodata(@_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub can_login {
|
||||
my ($self) = @_;
|
||||
# We return true if any method can log in.
|
||||
foreach my $object (@{$self->{_stack}}) {
|
||||
return 1 if $object->can_login;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub user_can_create_account {
|
||||
my ($self) = @_;
|
||||
# We return true if any method allows users to create accounts.
|
||||
foreach my $object (@{$self->{_stack}}) {
|
||||
return 1 if $object->user_can_create_account;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
1;
|
@ -1,111 +0,0 @@
|
||||
# -*- 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;
|
||||
}
|
||||
|
||||
# can_logout determines if a user may log out
|
||||
sub can_logout {
|
||||
return 1 if (login_class && login_class->can_logout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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)
|
||||
return $user if $user->id;
|
||||
|
||||
$type = LOGIN_REQUIRED if Bugzilla->cgi->param('GoAheadAndLogIn');
|
||||
$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);
|
||||
|
||||
# Redirect to SSL if required
|
||||
if (Param('sslbase') ne '' and Param('ssl') ne 'never') {
|
||||
Bugzilla->cgi->require_https(Param('sslbase'));
|
||||
}
|
||||
$user->set_flags('can_logout' => $class->can_logout);
|
||||
} else {
|
||||
Bugzilla->logout_request();
|
||||
}
|
||||
return $user;
|
||||
}
|
||||
|
||||
sub logout {
|
||||
my ($class, $user, $option) = @_;
|
||||
if (can_logout) {
|
||||
$class->login_class->logout($user, $option);
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Auth::Login::WWW - WWW login information gathering module
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=over
|
||||
|
||||
=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.
|
||||
|
||||
=back
|
@ -1,275 +0,0 @@
|
||||
# -*- 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;
|
||||
use Bugzilla::Token;
|
||||
|
||||
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;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
# First, try the actual login method against form variables
|
||||
my $username = trim($cgi->param("Bugzilla_login"));
|
||||
my $passwd = $cgi->param("Bugzilla_password");
|
||||
|
||||
$cgi->delete('Bugzilla_login', 'Bugzilla_password');
|
||||
|
||||
# Perform the actual authentication, get the method name from the class name
|
||||
my ($authmethod, $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 $logincookie = Bugzilla::Token::GenerateUniqueToken('logincookies', 'cookie');
|
||||
|
||||
$dbh->do("INSERT INTO logincookies (cookie, userid, ipaddr, lastused)
|
||||
VALUES (?, ?, ?, NOW())",
|
||||
undef,
|
||||
$logincookie, $userid, $ipaddr);
|
||||
|
||||
# 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') &&
|
||||
($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) {
|
||||
|
||||
# Redirect to SSL if required
|
||||
if (Param('sslbase') ne '' and Param('ssl') ne 'never') {
|
||||
$cgi->require_https(Param('sslbase'));
|
||||
}
|
||||
|
||||
# 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),
|
||||
'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.
|
||||
$dbh->do("DELETE FROM logincookies WHERE " .
|
||||
$dbh->sql_to_days('NOW()') . " - " .
|
||||
$dbh->sql_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_browser_cookies();
|
||||
# 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 });
|
||||
}
|
||||
|
||||
# This auth style allows the user to log out.
|
||||
sub can_logout { return 1; }
|
||||
|
||||
# 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;
|
||||
my $cgi = Bugzilla->cgi;
|
||||
$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 the current login cookie.
|
||||
# If a new cookie has been issued during this run, that's the current one.
|
||||
# If not, it's the one we've received.
|
||||
my $cookie;
|
||||
foreach (@{$cgi->{'Bugzilla_cookie_list'}}) {
|
||||
if ($_->name() eq 'Bugzilla_logincookie') {
|
||||
$cookie = $_->value();
|
||||
last;
|
||||
}
|
||||
}
|
||||
$cookie ||= $cgi->cookie("Bugzilla_logincookie");
|
||||
trick_taint($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->remove_cookie('Bugzilla_login');
|
||||
$cgi->remove_cookie('Bugzilla_logincookie');
|
||||
}
|
||||
|
||||
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>
|
@ -1,113 +0,0 @@
|
||||
# -*- 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=?";
|
||||
my @params = ($login_cookie, $login, $ipaddr);
|
||||
if (defined $netaddr) {
|
||||
trick_taint($netaddr);
|
||||
$query .= " OR logincookies.ipaddr=?";
|
||||
push(@params, $netaddr);
|
||||
}
|
||||
$query .= ")";
|
||||
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my ($userid, $disabledtext) = $dbh->selectrow_array($query, undef, @params);
|
||||
|
||||
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=NOW() 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>
|
@ -1,156 +0,0 @@
|
||||
# -*- 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::Env;
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Config;
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::Util;
|
||||
use Bugzilla::User;
|
||||
|
||||
sub login {
|
||||
my ($class, $type) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
# XXX This does not currently work correctly with Param('requirelogin').
|
||||
# Bug 253636 will hopefully see that param's needs taken care of in a
|
||||
# parent module, but for the time being, this module does not honor
|
||||
# the param in the way that CGI.pm does.
|
||||
|
||||
my $matched_userid;
|
||||
my $matched_extern_id;
|
||||
my $disabledtext;
|
||||
|
||||
# Gather the environment variables
|
||||
my $env_id = $ENV{Param("auth_env_id")} || '';
|
||||
my $env_email = $ENV{Param("auth_env_email")} || '';
|
||||
my $env_realname = $ENV{Param("auth_env_realname")} || '';
|
||||
|
||||
# make sure the email field contains only a valid email address
|
||||
my $emailregexp = Param("emailregexp");
|
||||
if ($env_email =~ /($emailregexp)/) {
|
||||
$env_email = $1;
|
||||
}
|
||||
else {
|
||||
$env_email = '';
|
||||
}
|
||||
|
||||
return undef unless $env_email;
|
||||
|
||||
# untaint the remaining values
|
||||
trick_taint($env_id);
|
||||
trick_taint($env_realname);
|
||||
|
||||
# Look in the DB for the extern_id
|
||||
if ($env_id) {
|
||||
($matched_userid, $disabledtext) =
|
||||
$dbh->selectrow_array('SELECT userid, disabledtext
|
||||
FROM profiles WHERE extern_id = ?',
|
||||
undef, $env_id);
|
||||
}
|
||||
|
||||
unless ($matched_userid) {
|
||||
# There was either no match for the external ID given, or one was
|
||||
# not present.
|
||||
#
|
||||
# Check to see if the email address is in there and has no
|
||||
# external id assigned. We test for both the login name (which we
|
||||
# also sent), and the id, so that we have a way of telling that we
|
||||
# got something instead of a bunch of NULLs
|
||||
($matched_extern_id, $matched_userid, $disabledtext) =
|
||||
$dbh->selectrow_array('SELECT extern_id, userid, disabledtext
|
||||
FROM profiles WHERE ' .
|
||||
$dbh->sql_istrcmp('login_name', '?'),
|
||||
undef, $env_email);
|
||||
|
||||
if ($matched_userid) {
|
||||
if ($matched_extern_id) {
|
||||
# someone with a different external ID has that address!
|
||||
ThrowUserError("extern_id_conflict");
|
||||
}
|
||||
else {
|
||||
# someone with no external ID used that address, time to
|
||||
# add the ID!
|
||||
$dbh->do('UPDATE profiles SET extern_id = ? WHERE userid = ?',
|
||||
undef,($env_id, $matched_userid));
|
||||
}
|
||||
}
|
||||
else {
|
||||
# Need to create a new user with that email address. Note
|
||||
# that cryptpassword has been filled in with '*', since the
|
||||
# user has no DB password.
|
||||
insert_new_user($env_email, $env_realname, '*');
|
||||
my $new_user = Bugzilla::User->new_from_login($env_email);
|
||||
$matched_userid = $new_user->id;
|
||||
}
|
||||
}
|
||||
|
||||
# now that we hopefully have a username, we need to see if the data
|
||||
# has to be updated. If we just created this account, then the data
|
||||
# is already up to date.
|
||||
my ($username, $this_realname) =
|
||||
$dbh->selectrow_array('SELECT login_name, realname
|
||||
FROM profiles WHERE userid = ?',
|
||||
undef, $matched_userid);
|
||||
|
||||
if (($username ne $env_email) || ($this_realname ne $env_realname)) {
|
||||
$dbh->do('UPDATE profiles SET login_name = ?, realname = ?
|
||||
WHERE userid = ?', undef,
|
||||
($env_email, ($env_realname || $this_realname), $matched_userid));
|
||||
|
||||
# If the login name may be new, make sure the regexp groups are current
|
||||
my $userprofile = new Bugzilla::User($matched_userid);
|
||||
$userprofile->derive_regexp_groups;
|
||||
}
|
||||
|
||||
# Now we throw an error if the user has been disabled
|
||||
if ($disabledtext) {
|
||||
ThrowUserError("account_disabled",
|
||||
{'disabled_reason' => $disabledtext});
|
||||
}
|
||||
|
||||
return $matched_userid;
|
||||
}
|
||||
|
||||
# This auth style does not allow the user to log out.
|
||||
sub can_logout { return 0; }
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Auth::Env - Environment Variable Authentication
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Many external user authentication systems supply login information to CGI
|
||||
programs via environment variables. This module checks to see if those
|
||||
variables are populated and, if so, assumes authentication was successful and
|
||||
returns the user's ID, having automatically created a new profile if
|
||||
necessary.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<Bugzilla::Auth>
|
153
webtools/bugzilla/Bugzilla/Auth/Persist/Cookie.pm
Normal file
153
webtools/bugzilla/Bugzilla/Auth/Persist/Cookie.pm
Normal file
@ -0,0 +1,153 @@
|
||||
# -*- 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>
|
||||
# Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
|
||||
package Bugzilla::Auth::Persist::Cookie;
|
||||
use strict;
|
||||
use fields qw();
|
||||
|
||||
use Bugzilla::Config;
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Util;
|
||||
use Bugzilla::User;
|
||||
|
||||
use List::Util qw(first);
|
||||
|
||||
sub new {
|
||||
my ($class) = @_;
|
||||
my $self = fields::new($class);
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub persist_login {
|
||||
my ($self, $user) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $cgi = Bugzilla->cgi;
|
||||
|
||||
my $ip_addr = $cgi->remote_addr;
|
||||
unless ($cgi->param('Bugzilla_restrictlogin') ||
|
||||
Param('loginnetmask') == 32)
|
||||
{
|
||||
# XXX I don't like this subclass being dependent upon its parent.
|
||||
$ip_addr = Bugzilla::Auth::get_netaddr($ip_addr);
|
||||
}
|
||||
|
||||
# The IP address is valid, at least for comparing with itself in a
|
||||
# subsequent login
|
||||
trick_taint($ip_addr);
|
||||
|
||||
my $login_cookie =
|
||||
Bugzilla::Token::GenerateUniqueToken('logincookies', 'cookie');
|
||||
|
||||
$dbh->do("INSERT INTO logincookies (cookie, userid, ipaddr, lastused)
|
||||
VALUES (?, ?, ?, NOW())",
|
||||
undef, $login_cookie, $user->id, $ip_addr);
|
||||
|
||||
# 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') &&
|
||||
$cgi->param('Bugzilla_remember') eq 'on') )
|
||||
{
|
||||
$cgi->send_cookie(-name => 'Bugzilla_login',
|
||||
-value => $user->id,
|
||||
-expires => 'Fri, 01-Jan-2038 00:00:00 GMT');
|
||||
$cgi->send_cookie(-name => 'Bugzilla_logincookie',
|
||||
-value => $login_cookie,
|
||||
-expires => 'Fri, 01-Jan-2038 00:00:00 GMT');
|
||||
|
||||
}
|
||||
else {
|
||||
$cgi->send_cookie(-name => 'Bugzilla_login',
|
||||
-value => $user->id);
|
||||
$cgi->send_cookie(-name => 'Bugzilla_logincookie',
|
||||
-value => $login_cookie);
|
||||
}
|
||||
}
|
||||
|
||||
sub logout {
|
||||
my ($self, $param) = @_;
|
||||
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $cgi = Bugzilla->cgi;
|
||||
$param = {} unless $param;
|
||||
my $user = $param->{user} || Bugzilla->user;
|
||||
my $type = $param->{type} || LOGOUT_ALL;
|
||||
|
||||
if ($type == LOGOUT_ALL) {
|
||||
$dbh->do("DELETE FROM logincookies WHERE userid = ?",
|
||||
undef, $user->id);
|
||||
return;
|
||||
}
|
||||
|
||||
# The LOGOUT_*_CURRENT options require the current login cookie.
|
||||
# If a new cookie has been issued during this run, that's the current one.
|
||||
# If not, it's the one we've received.
|
||||
my $cookie = first {$_->name eq 'Bugzilla_logincookie'}
|
||||
@{$cgi->{'Bugzilla_cookie_list'}};
|
||||
my $login_cookie;
|
||||
if ($cookie) {
|
||||
$login_cookie = $cookie->value;
|
||||
}
|
||||
else {
|
||||
$login_cookie = $cgi->cookie("Bugzilla_logincookie");
|
||||
}
|
||||
trick_taint($login_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 ($type == LOGOUT_KEEP_CURRENT) {
|
||||
$dbh->do("DELETE FROM logincookies WHERE cookie != ? AND userid = ?",
|
||||
undef, $login_cookie, $user->id);
|
||||
} elsif ($type == LOGOUT_CURRENT) {
|
||||
$dbh->do("DELETE FROM logincookies WHERE cookie = ? AND userid = ?",
|
||||
undef, $login_cookie, $user->id);
|
||||
} else {
|
||||
die("Invalid type $type supplied to logout()");
|
||||
}
|
||||
|
||||
if ($type != LOGOUT_KEEP_CURRENT) {
|
||||
clear_browser_cookies();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sub clear_browser_cookies {
|
||||
my $cgi = Bugzilla->cgi;
|
||||
$cgi->remove_cookie('Bugzilla_login');
|
||||
$cgi->remove_cookie('Bugzilla_logincookie');
|
||||
}
|
||||
|
||||
1;
|
@ -1,132 +0,0 @@
|
||||
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.
|
||||
|
223
webtools/bugzilla/Bugzilla/Auth/Verify.pm
Normal file
223
webtools/bugzilla/Bugzilla/Auth/Verify.pm
Normal file
@ -0,0 +1,223 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# Contributor(s): Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
|
||||
package Bugzilla::Auth::Verify;
|
||||
|
||||
use strict;
|
||||
use fields qw();
|
||||
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::User;
|
||||
use Bugzilla::Util;
|
||||
|
||||
use constant user_can_create_account => 1;
|
||||
|
||||
sub new {
|
||||
my ($class, $login_type) = @_;
|
||||
my $self = fields::new($class);
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub can_change_password {
|
||||
return $_[0]->can('change_password');
|
||||
}
|
||||
|
||||
sub create_or_update_user {
|
||||
my ($self, $params) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
my $extern_id = $params->{extern_id};
|
||||
my $username = $params->{bz_username} || $params->{username};
|
||||
my $password = $params->{password} || '*';
|
||||
my $real_name = $params->{realname} || '';
|
||||
my $user_id = $params->{user_id};
|
||||
|
||||
# A passed-in user_id always overrides anything else, for determining
|
||||
# what account we should return.
|
||||
if (!$user_id) {
|
||||
my $username_user_id = login_to_id($username || '');
|
||||
my $extern_user_id;
|
||||
if ($extern_id) {
|
||||
trick_taint($extern_id);
|
||||
$extern_user_id = $dbh->selectrow_array('SELECT userid
|
||||
FROM profiles WHERE extern_id = ?', undef, $extern_id);
|
||||
}
|
||||
|
||||
# If we have both a valid extern_id and a valid username, and they are
|
||||
# not the same id, then we have a conflict.
|
||||
if ($username_user_id && $extern_user_id
|
||||
&& $username_user_id ne $extern_user_id)
|
||||
{
|
||||
my $extern_name = Bugzilla::User->new($extern_user_id)->login;
|
||||
return { failure => AUTH_ERROR, error => "extern_id_conflict",
|
||||
details => {extern_id => $extern_id,
|
||||
extern_user => $extern_name,
|
||||
username => $username} };
|
||||
}
|
||||
|
||||
# If we have a valid username, but no valid id,
|
||||
# then we have to create the user. This happens when we're
|
||||
# passed only a username, and that username doesn't exist already.
|
||||
if ($username && !$username_user_id && !$extern_user_id) {
|
||||
validate_email_syntax($username)
|
||||
|| return { failure => AUTH_ERROR,
|
||||
error => 'auth_invalid_email',
|
||||
details => {addr => $username} };
|
||||
insert_new_user($username, $real_name, $password);
|
||||
$username_user_id = login_to_id($username);
|
||||
}
|
||||
|
||||
# If we have a valid username id and an extern_id, but no valid
|
||||
# extern_user_id, then we have to set the user's extern_id.
|
||||
if ($extern_id && $username_user_id && !$extern_user_id) {
|
||||
$dbh->do('UPDATE profiles SET extern_id = ? WHERE userid = ?',
|
||||
undef, $extern_id, $username_user_id);
|
||||
}
|
||||
|
||||
# Finally, at this point, one of these will give us a valid user id.
|
||||
$user_id = $extern_user_id || $username_user_id;
|
||||
}
|
||||
|
||||
# If we still don't have a valid user_id, then we weren't passed
|
||||
# enough information in $params, and we should die right here.
|
||||
ThrowCodeError('bad_arg', {argument => 'params', function =>
|
||||
'Bugzilla::Auth::Verify::create_or_update_user'})
|
||||
unless $user_id;
|
||||
|
||||
my $user = new Bugzilla::User($user_id);
|
||||
|
||||
# Now that we have a valid User, we need to see if any data has to be
|
||||
# updated.
|
||||
if ($username && $user->login ne $username) {
|
||||
validate_email_syntax($username)
|
||||
|| return { failure => AUTH_ERROR, error => 'auth_invalid_email',
|
||||
details => {addr => $username} };
|
||||
$dbh->do('UPDATE profiles SET login_name = ? WHERE userid = ?',
|
||||
$username, $user->id);
|
||||
}
|
||||
if ($real_name && $user->realname ne $real_name) {
|
||||
$dbh->do('UPDATE profiles SET realname = ? WHERE userid = ?',
|
||||
undef, $real_name, $user->id);
|
||||
}
|
||||
|
||||
return { user => $user };
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Auth::Verify - An object that verifies usernames and passwords.
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Bugzilla::Auth::Verify provides the "Verifier" part of the Bugzilla
|
||||
login process. (For details, see the "STRUCTURE" section of
|
||||
L<Bugzilla::Auth>.)
|
||||
|
||||
It is mostly an abstract class, requiring subclasses to implement
|
||||
most methods.
|
||||
|
||||
Note that callers outside of the C<Bugzilla::Auth> package should never
|
||||
create this object directly. Just create a C<Bugzilla::Auth> object
|
||||
and call C<login> on it.
|
||||
|
||||
=head1 VERIFICATION METHODS
|
||||
|
||||
These are the methods that have to do with the actual verification.
|
||||
|
||||
Subclasses MUST implement these methods.
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<check_credentials($login_data)>
|
||||
|
||||
Description: Checks whether or not a username is valid.
|
||||
Params: $login_data - A C<$login_data> hashref, as described in
|
||||
L<Bugzilla::Auth>.
|
||||
This C<$login_data> hashref MUST contain
|
||||
C<username>, and SHOULD also contain
|
||||
C<password>.
|
||||
Returns: A C<$login_data> hashref with C<bz_username> set. This
|
||||
method may also set C<realname>. It must avoid changing
|
||||
anything that is already set.
|
||||
|
||||
=back
|
||||
|
||||
=head1 MODIFICATION METHODS
|
||||
|
||||
These are methods that change data in the actual authentication backend.
|
||||
|
||||
These methods are optional, they do not have to be implemented by
|
||||
subclasses.
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<create_or_update_user($login_data)>
|
||||
|
||||
Description: Automatically creates a user account in the database
|
||||
if it doesn't already exist, or updates the account
|
||||
data if C<$login_data> contains newer information.
|
||||
|
||||
Params: $login_data - A C<$login_data> hashref, as described in
|
||||
L<Bugzilla::Auth>.
|
||||
This C<$login_data> hashref MUST contain
|
||||
either C<user_id>, C<bz_username>, or
|
||||
C<username>. If both C<username> and C<bz_username>
|
||||
are specified, C<bz_username> is used as the
|
||||
login name of the user to create in the database.
|
||||
It MAY also contain C<extern_id>, in which
|
||||
case it still MUST contain C<bz_username> or
|
||||
C<username>.
|
||||
It MAY contain C<password> and C<realname>.
|
||||
|
||||
Returns: A hashref with one element, C<user>, which is a
|
||||
L<Bugzilla::User> object. May also return a login error
|
||||
as described in L<Bugzilla::Auth>.
|
||||
|
||||
Note: This method is not abstract, it is actually implemented
|
||||
and creates accounts in the Bugzilla database. Subclasses
|
||||
should probably all call the C<Bugzilla::Auth::Verify>
|
||||
version of this function at the end of their own
|
||||
C<create_or_update_user>.
|
||||
|
||||
=item C<change_password($user, $password)>
|
||||
|
||||
Description: Modifies the user's password in the authentication backend.
|
||||
Params: $user - A L<Bugzilla::User> object representing the user
|
||||
whose password we want to change.
|
||||
$password - The user's new password.
|
||||
Returns: Nothing.
|
||||
|
||||
=back
|
||||
|
||||
=head1 INFO METHODS
|
||||
|
||||
These are methods that describe the capabilities of this object.
|
||||
These are all no-parameter methods that return either C<true> or
|
||||
C<false>.
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<user_can_create_account>
|
||||
|
||||
Whether or not users can manually create accounts in this type of
|
||||
account source. Defaults to C<true>.
|
||||
|
||||
=back
|
@ -28,97 +28,51 @@
|
||||
# Erik Stambaugh <erik@dasbistro.com>
|
||||
|
||||
package Bugzilla::Auth::Verify::DB;
|
||||
|
||||
use strict;
|
||||
use base qw(Bugzilla::Auth::Verify);
|
||||
|
||||
use Bugzilla::Config;
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Token;
|
||||
use Bugzilla::Util;
|
||||
use Bugzilla::User;
|
||||
|
||||
my $edit_options = {
|
||||
'new' => 1,
|
||||
'userid' => 0,
|
||||
'login_name' => 1,
|
||||
'realname' => 1,
|
||||
};
|
||||
sub check_credentials {
|
||||
my ($self, $login_data) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
sub can_edit {
|
||||
my ($class, $type) = @_;
|
||||
return $edit_options->{$type};
|
||||
}
|
||||
my $username = $login_data->{username};
|
||||
my $user_id = login_to_id($username);
|
||||
|
||||
sub authenticate {
|
||||
my ($class, $username, $passwd) = @_;
|
||||
return { failure => AUTH_NO_SUCH_USER } unless $user_id;
|
||||
|
||||
return (AUTH_NODATA) unless defined $username && defined $passwd;
|
||||
$login_data->{bz_username} = $username;
|
||||
my $password = $login_data->{password};
|
||||
|
||||
my $userid = Bugzilla::User::login_to_id($username);
|
||||
return (AUTH_LOGINFAILED) unless $userid;
|
||||
trick_taint($username);
|
||||
my ($real_password_crypted) = $dbh->selectrow_array(
|
||||
"SELECT cryptpassword FROM profiles WHERE userid = ?",
|
||||
undef, $user_id);
|
||||
|
||||
return (AUTH_LOGINFAILED, $userid)
|
||||
unless $class->check_password($userid, $passwd);
|
||||
# Using the internal crypted password as the salt,
|
||||
# crypt the password the user entered.
|
||||
my $entered_password_crypted = crypt($password, $real_password_crypted);
|
||||
|
||||
return { failure => AUTH_LOGINFAILED }
|
||||
if $entered_password_crypted ne $real_password_crypted;
|
||||
|
||||
# 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");
|
||||
Bugzilla::Token::DeletePasswordTokens($user_id, "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_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;
|
||||
return $login_data;
|
||||
}
|
||||
|
||||
sub change_password {
|
||||
my ($class, $userid, $password) = @_;
|
||||
my ($self, $user, $password) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $cryptpassword = bz_crypt($password);
|
||||
$dbh->do("UPDATE profiles SET cryptpassword = ? WHERE userid = ?",
|
||||
undef, $cryptpassword, $userid);
|
||||
$dbh->do("UPDATE profiles SET cryptpassword = ? WHERE userid = ?",
|
||||
undef, $cryptpassword, $user->id);
|
||||
}
|
||||
|
||||
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>
|
||||
|
@ -26,39 +26,30 @@
|
||||
# Christian Reis <kiko@async.com.br>
|
||||
# Bradley Baetz <bbaetz@acm.org>
|
||||
# Erik Stambaugh <erik@dasbistro.com>
|
||||
# Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
|
||||
package Bugzilla::Auth::Verify::LDAP;
|
||||
|
||||
use strict;
|
||||
use base qw(Bugzilla::Auth::Verify);
|
||||
use fields qw(
|
||||
ldap
|
||||
);
|
||||
|
||||
use Bugzilla::Config;
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::User;
|
||||
use Bugzilla::Error;
|
||||
|
||||
use Net::LDAP;
|
||||
|
||||
my $edit_options = {
|
||||
'new' => 0,
|
||||
'userid' => 0,
|
||||
'login_name' => 0,
|
||||
'realname' => 0,
|
||||
};
|
||||
use constant DEFAULT_PORT => 389;
|
||||
use constant DEFAULT_SSL_PORT => 636;
|
||||
|
||||
sub can_edit {
|
||||
my ($class, $type) = @_;
|
||||
return $edit_options->{$type};
|
||||
}
|
||||
use constant admin_can_create_account => 0;
|
||||
use constant user_can_create_account => 0;
|
||||
|
||||
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;
|
||||
sub check_credentials {
|
||||
my ($self, $params) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
# We need to bind anonymously to the LDAP server. This is
|
||||
# because we need to get the Distinguished Name of the user trying
|
||||
@ -67,151 +58,108 @@ sub authenticate {
|
||||
# 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");
|
||||
$self->_bind_ldap_anonymously();
|
||||
|
||||
# Now, we verify that the user exists, and get a LDAP Distinguished
|
||||
# Name for the user.
|
||||
my $username = $params->{username};
|
||||
my $dn_result = $self->ldap->search(_bz_search_params($username),
|
||||
attrs => ['dn']);
|
||||
return { failure => AUTH_ERROR, error => "ldap_search_error",
|
||||
details => {errstr => $dn_result->error, username => $username}
|
||||
} if $dn_result->code;
|
||||
|
||||
return { failure => AUTH_NO_SUCH_USER } if !$dn_result->count;
|
||||
|
||||
my $dn = $dn_result->shift_entry->dn;
|
||||
|
||||
# Check the password.
|
||||
my $pw_result = $self->ldap->bind($dn, password => $params->{password});
|
||||
return { failure => AUTH_LOGINFAILED } if $pw_result->code;
|
||||
|
||||
# And now we fill in the user's details.
|
||||
my $detail_result = $self->ldap->search(_bz_search_params($username));
|
||||
return { failure => AUTH_ERROR, error => "ldap_search_error",
|
||||
details => {errstr => $detail_result->error, username => $username}
|
||||
} if $detail_result->code;
|
||||
|
||||
my $user_entry = $detail_result->shift_entry;
|
||||
|
||||
my $mail_attr = Param("LDAPmailattribute");
|
||||
if (!$user_entry->exists($mail_attr)) {
|
||||
return { failure => AUTH_ERROR,
|
||||
error => "ldap_cannot_retreive_attr",
|
||||
details => {attr => $mail_attr} };
|
||||
}
|
||||
|
||||
my $LDAPport = "389"; # default LDAP port
|
||||
my $LDAPprotocol = "ldap";
|
||||
$params->{bz_username} = $user_entry->get_value($mail_attr);
|
||||
$params->{realname} ||= $user_entry->get_value("displayName");
|
||||
$params->{realname} ||= $user_entry->get_value("cn");
|
||||
|
||||
if ($LDAPserver =~ /(ldap|ldaps):\/\/(.*)/) {
|
||||
# ldap(s)://server(:port)
|
||||
$LDAPprotocol = $1;
|
||||
my $serverpart = $2;
|
||||
if ($serverpart =~ /:/) {
|
||||
# ldap(s)://server:port
|
||||
($LDAPserver, $LDAPport) = split(":", $serverpart);
|
||||
} else {
|
||||
# ldap(s)://server
|
||||
$LDAPserver = $serverpart;
|
||||
if ($LDAPprotocol eq "ldaps") {
|
||||
$LDAPport = "636";
|
||||
}
|
||||
}
|
||||
} elsif ($LDAPserver =~ /:/) {
|
||||
# server:port
|
||||
($LDAPserver, $LDAPport) = split(":", $LDAPserver);
|
||||
}
|
||||
return $params;
|
||||
}
|
||||
|
||||
sub _bz_search_params {
|
||||
my ($username) = @_;
|
||||
return (base => Param("LDAPBaseDN"),
|
||||
scope => "sub",
|
||||
filter => '(&(' . Param("LDAPuidattribute") . "=$username)"
|
||||
. Param("LDAPfilter") . ')');
|
||||
}
|
||||
|
||||
my $LDAPconn = Net::LDAP->new("$LDAPprotocol://$LDAPserver:$LDAPport", version => 3);
|
||||
if(!$LDAPconn) {
|
||||
return (AUTH_ERROR, undef, "connect_failed");
|
||||
}
|
||||
|
||||
my $mesg;
|
||||
sub _bind_ldap_anonymously {
|
||||
my ($self) = @_;
|
||||
my $bind_result;
|
||||
if (Param("LDAPbinddn")) {
|
||||
my ($LDAPbinddn,$LDAPbindpass) = split(":",Param("LDAPbinddn"));
|
||||
$mesg = $LDAPconn->bind($LDAPbinddn, password => $LDAPbindpass);
|
||||
$bind_result =
|
||||
$self->ldap->bind($LDAPbinddn, password => $LDAPbindpass);
|
||||
}
|
||||
else {
|
||||
$mesg = $LDAPconn->bind();
|
||||
}
|
||||
if($mesg->code) {
|
||||
return (AUTH_ERROR, undef,
|
||||
"connect_failed",
|
||||
{ errstr => $mesg->error });
|
||||
$bind_result = $self->ldap->bind();
|
||||
}
|
||||
ThrowCodeError("ldap_bind_failed", {errstr => $bind_result->error})
|
||||
if $bind_result->code;
|
||||
}
|
||||
|
||||
# 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;
|
||||
# We can't just do this in new(), because we're not allowed to throw any
|
||||
# error from anywhere under Bugzilla::Auth::new -- otherwise we
|
||||
# could create a situation where the admin couldn't get to editparams
|
||||
# to fix his mistake. (Because Bugzilla->login always calls
|
||||
# Bugzilla::Auth->new, and almost every page calls Bugzilla->login.)
|
||||
sub ldap {
|
||||
my ($self) = @_;
|
||||
return $self->{ldap} if $self->{ldap};
|
||||
|
||||
# Now we get the DN from this search.
|
||||
my $userDN = $mesg->shift_entry->dn;
|
||||
my $server = Param("LDAPserver");
|
||||
ThrowCodeError("ldap_server_not_defined") unless $server;
|
||||
|
||||
# Now we attempt to bind as the specified user.
|
||||
$mesg = $LDAPconn->bind( $userDN, password => $passwd);
|
||||
my $port = DEFAULT_PORT;
|
||||
my $protocol = "ldap";
|
||||
|
||||
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 " .
|
||||
$dbh->sql_istrcmp('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");
|
||||
if ($server =~ /(ldap|ldaps):\/\/(.*)/) {
|
||||
# ldap(s)://server(:port)
|
||||
$protocol = $1;
|
||||
my $server_part = $2;
|
||||
if ($server_part =~ /:/) {
|
||||
# ldap(s)://server:port
|
||||
($server, $port) = split(":", $server_part);
|
||||
} else {
|
||||
# ldap(s)://server
|
||||
$server = $server_part;
|
||||
if ($protocol eq "ldaps") {
|
||||
$port = DEFAULT_SSL_PORT;
|
||||
}
|
||||
}
|
||||
insert_new_user($username, $userRealName);
|
||||
|
||||
($userid, $disabledtext) = $dbh->selectrow_array($sth,
|
||||
undef,
|
||||
$username);
|
||||
return (AUTH_ERROR, $userid, "no_userid")
|
||||
unless $userid;
|
||||
} elsif ($server =~ /:/) {
|
||||
# server:port
|
||||
($server, $port) = split(":", $server);
|
||||
}
|
||||
|
||||
# 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);
|
||||
my $conn_string = "$protocol://$server:$port";
|
||||
$self->{ldap} = new Net::LDAP($conn_string)
|
||||
|| ThrowCodeError("ldap_connect_failed", { server => $conn_string });
|
||||
return $self->{ldap};
|
||||
}
|
||||
|
||||
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:E<sol>E<sol>bugzilla.mozilla.orgE<sol>> 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>
|
||||
|
81
webtools/bugzilla/Bugzilla/Auth/Verify/Stack.pm
Normal file
81
webtools/bugzilla/Bugzilla/Auth/Verify/Stack.pm
Normal file
@ -0,0 +1,81 @@
|
||||
# -*- 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.
|
||||
#
|
||||
# Contributor(s): Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
|
||||
package Bugzilla::Auth::Verify::Stack;
|
||||
use strict;
|
||||
use base qw(Bugzilla::Auth::Verify);
|
||||
use fields qw(
|
||||
_stack
|
||||
successful
|
||||
);
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $list = shift;
|
||||
my $self = $class->SUPER::new(@_);
|
||||
$self->{_stack} = [];
|
||||
foreach my $verify_method (split(',', $list)) {
|
||||
require "Bugzilla/Auth/Verify/${verify_method}.pm";
|
||||
push(@{$self->{_stack}},
|
||||
"Bugzilla::Auth::Verify::$verify_method"->new(@_));
|
||||
}
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub can_change_password {
|
||||
my ($self) = @_;
|
||||
# We return true if any method can change passwords.
|
||||
foreach my $object (@{$self->{_stack}}) {
|
||||
return 1 if $object->can_change_password;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub check_credentials {
|
||||
my $self = shift;
|
||||
my $result;
|
||||
foreach my $object (@{$self->{_stack}}) {
|
||||
$result = $object->check_credentials(@_);
|
||||
$self->{successful} = $object;
|
||||
last if !$result->{failure};
|
||||
# So that if none of them succeed, it's undef.
|
||||
$self->{successful} = undef;
|
||||
}
|
||||
# Returns the result at the bottom of the stack if they all fail.
|
||||
return $result;
|
||||
}
|
||||
|
||||
sub create_or_update_user {
|
||||
my $self = shift;
|
||||
my $result;
|
||||
foreach my $object (@{$self->{_stack}}) {
|
||||
$result = $object->create_or_update_user(@_);
|
||||
last if !$result->{failure};
|
||||
}
|
||||
# Returns the result at the bottom of the stack if they all fail.
|
||||
return $result;
|
||||
}
|
||||
|
||||
sub user_can_create_account {
|
||||
my ($self) = @_;
|
||||
# We return true if any method allows the user to create an account.
|
||||
foreach my $object (@{$self->{_stack}}) {
|
||||
return 1 if $object->user_can_create_account;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
1;
|
@ -43,6 +43,7 @@ use base qw(Exporter);
|
||||
AUTH_ERROR
|
||||
AUTH_LOGINFAILED
|
||||
AUTH_DISABLED
|
||||
AUTH_NO_SUCH_USER
|
||||
|
||||
USER_PASSWORD_MIN_LENGTH
|
||||
USER_PASSWORD_MAX_LENGTH
|
||||
@ -144,6 +145,7 @@ use constant AUTH_NODATA => 1;
|
||||
use constant AUTH_ERROR => 2;
|
||||
use constant AUTH_LOGINFAILED => 3;
|
||||
use constant AUTH_DISABLED => 4;
|
||||
use constant AUTH_NO_SUCH_USER => 5;
|
||||
|
||||
# The minimum and maximum lengths a password must have.
|
||||
use constant USER_PASSWORD_MIN_LENGTH => 3;
|
||||
|
@ -155,17 +155,17 @@ sub disabledtext { $_[0]->{'disabledtext'}; }
|
||||
sub is_disabled { $_[0]->disabledtext ? 1 : 0; }
|
||||
sub showmybugslink { $_[0]->{showmybugslink}; }
|
||||
|
||||
sub set_flags {
|
||||
my $self = shift;
|
||||
while (my $key = shift) {
|
||||
$self->{'flags'}->{$key} = shift;
|
||||
}
|
||||
sub set_authorizer {
|
||||
my ($self, $authorizer) = @_;
|
||||
$self->{authorizer} = $authorizer;
|
||||
}
|
||||
|
||||
sub get_flag {
|
||||
my $self = shift;
|
||||
my $key = shift;
|
||||
return $self->{'flags'}->{$key};
|
||||
sub authorizer {
|
||||
my ($self) = @_;
|
||||
if (!$self->{authorizer}) {
|
||||
require Bugzilla::Auth;
|
||||
$self->{authorizer} = new Bugzilla::Auth();
|
||||
}
|
||||
return $self->{authorizer};
|
||||
}
|
||||
|
||||
# Generate a string to identify the user by name + login if the user
|
||||
@ -1505,6 +1505,17 @@ which to identify the user. Currently the part of the user's email address
|
||||
before the at sign (@), but that could change, especially if we implement
|
||||
usernames not dependent on email address.
|
||||
|
||||
=item C<authorizer>
|
||||
|
||||
This is the L<Bugzilla::Auth> object that the User logged in with.
|
||||
If the user hasn't logged in yet, a new, empty Bugzilla::Auth() object is
|
||||
returned.
|
||||
|
||||
=item C<set_authorizer($authorizer)>
|
||||
|
||||
Sets the L<Bugzilla::Auth> object to be returned by C<authorizer()>.
|
||||
Should only be called by C<Bugzilla::Auth::login>, for the most part.
|
||||
|
||||
=item C<queries>
|
||||
|
||||
Returns an array of the user's named queries, sorted in a case-insensitive
|
||||
@ -1718,21 +1729,6 @@ When called with one argument:
|
||||
Returns C<1> if the user can bless the group with that name, returns
|
||||
C<0> otherwise.
|
||||
|
||||
=item C<set_flags>
|
||||
=item C<get_flag>
|
||||
|
||||
User flags are template-accessible user status information, stored in the form
|
||||
of a hash. For an example of use, when the current user is authenticated in
|
||||
such a way that they are allowed to log out, the 'can_logout' flag is set to
|
||||
true (1). The template then checks this flag before displaying the "Log Out"
|
||||
link.
|
||||
|
||||
C<set_flags> is called with any number of key,value pairs. Flags for each key
|
||||
will be set to the specified value.
|
||||
|
||||
C<get_flag> is called with a single key name, which returns the associated
|
||||
value.
|
||||
|
||||
=item C<wants_bug_mail>
|
||||
|
||||
Returns true if the user wants mail for a given bug change.
|
||||
|
@ -49,7 +49,7 @@ my $vars = {};
|
||||
print $cgi->header();
|
||||
|
||||
# If we're using LDAP for login, then we can't create a new account here.
|
||||
unless (Bugzilla::Auth->can_edit('new')) {
|
||||
unless (Bugzilla->user->authorizer->user_can_create_account) {
|
||||
ThrowUserError("auth_cant_create_account");
|
||||
}
|
||||
|
||||
|
@ -53,15 +53,7 @@ my $template = Bugzilla->template;
|
||||
my $vars = {};
|
||||
my $buffer = $cgi->query_string();
|
||||
|
||||
if ($cgi->param("GoAheadAndLogIn")) {
|
||||
# We got here from a login page, probably from relogin.cgi. We better
|
||||
# make sure the password is legit.
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
} else {
|
||||
Bugzilla->login();
|
||||
}
|
||||
|
||||
my $user = Bugzilla->user;
|
||||
my $user = Bugzilla->login();
|
||||
my $userid = $user->id;
|
||||
|
||||
# Backwards compatibility hack -- if there are any of the old QUERY_*
|
||||
|
@ -75,8 +75,7 @@ elsif ($action eq 'begin-sudo') {
|
||||
# and password.
|
||||
# We only need to do this for authentication methods that involve Bugzilla
|
||||
# directly obtaining a login (i.e. normal CGI login), as opposed to other
|
||||
# methods (like Environment vars login). We assume that if a user can log
|
||||
# out, they can also log in:
|
||||
# methods (like Environment vars login).
|
||||
|
||||
# First, record if Bugzilla_login and Bugzilla_password were provided
|
||||
my $credentials_provided;
|
||||
@ -92,7 +91,7 @@ elsif ($action eq 'begin-sudo') {
|
||||
# At this point, the user is logged in. However, if they used a method
|
||||
# where they could have provided a username/password (i.e. CGI), but they
|
||||
# did not provide a username/password, then throw an error.
|
||||
if ($user->get_flag('can_logout') && !$credentials_provided) {
|
||||
if ($user->authorizer->can_login && !$credentials_provided) {
|
||||
ThrowUserError('sudo_password_required',
|
||||
{ target_login => $cgi->param('target_login'),
|
||||
reason => $cgi->param('reason')});
|
||||
|
@ -37,11 +37,7 @@ my $cgi = Bugzilla->cgi;
|
||||
my $template = Bugzilla->template;
|
||||
my $vars = {};
|
||||
|
||||
if ($cgi->param('GoAheadAndLogIn')) {
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
} else {
|
||||
Bugzilla->login();
|
||||
}
|
||||
Bugzilla->login();
|
||||
|
||||
# Editable, 'single' HTML bugs are treated slightly specially in a few places
|
||||
my $single = !$cgi->param('format')
|
||||
|
@ -1,52 +0,0 @@
|
||||
[%# 1.0@bugzilla.org %]
|
||||
[%# 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): Bradley Baetz <bbaetz@acm.org>
|
||||
#%]
|
||||
|
||||
[%# INTERFACE:
|
||||
# auth_err_tag: string. The tag for the error
|
||||
# info: hash. Additional variables which may be used when printing details
|
||||
# of the error.
|
||||
#%]
|
||||
|
||||
[% PROCESS global/variables.none.tmpl %]
|
||||
|
||||
[% admindocslinks = {'extraconfig.html#bzldap' => 'Setting up LDAP authentication'} %]
|
||||
|
||||
[% SWITCH auth_err_tag %]
|
||||
[% CASE "cannot_retreive_attr" %]
|
||||
The specified LDAP attribute [% info.attr FILTER html %] was not found.
|
||||
|
||||
[% CASE "connect_failed" %]
|
||||
An error occurred while trying to connect to the LDAP server.
|
||||
[% IF info.errstr %]
|
||||
The error from the server was: <tt>[% info.errstr FILTER html %]</tt>.
|
||||
[% END %]
|
||||
|
||||
[% CASE "no_userid" %]
|
||||
[% terms.Bugzilla %] created a new account for you, but then could not find the
|
||||
new userid.
|
||||
|
||||
[% CASE "server_not_defined" %]
|
||||
The LDAP server for authentication has not been defined.
|
||||
|
||||
[% CASE %]
|
||||
Unhandled authentication error: <tt>[% auth_err_tag FILTER html %]</tt>
|
||||
|
||||
[% END %]
|
@ -79,7 +79,7 @@
|
||||
[%# For now, password change requests only apply to the DB
|
||||
# verification method #%]
|
||||
|
||||
[% IF has_db != 0 %]
|
||||
[% IF user.authorizer.can_change_password %]
|
||||
<td>[ <a href="index.cgi?GoAheadAndLogIn=1#forgot">Forgot my Password</a> ]</td>
|
||||
[% END %]
|
||||
</tr>
|
||||
|
@ -22,7 +22,6 @@
|
||||
|
||||
[%# INTERFACE:
|
||||
# target: string. URL to go to after login.
|
||||
# has_db: true if DB is one of the available authentication mechanisms
|
||||
#%]
|
||||
|
||||
[% PROCESS global/variables.none.tmpl %]
|
||||
@ -100,9 +99,7 @@
|
||||
# their password, assuming that our auth method allows that.
|
||||
#%]
|
||||
|
||||
[% IF caneditaccount %]
|
||||
|
||||
[% IF Param("createemailregexp") %]
|
||||
[% IF Param("createemailregexp") && user.authorizer.user_can_create_account %]
|
||||
<hr>
|
||||
|
||||
<p>
|
||||
@ -111,10 +108,7 @@
|
||||
</p>
|
||||
[% END %]
|
||||
|
||||
[%# For now, password change requests only apply to the DB
|
||||
# verification method #%]
|
||||
|
||||
[% IF has_db != 0 %]
|
||||
[% IF user.authorizer.can_change_password %]
|
||||
<hr>
|
||||
|
||||
<a name="forgot"></a>
|
||||
@ -126,10 +120,6 @@
|
||||
<input size="35" name="loginname">
|
||||
<input type="submit" value="Submit Request">
|
||||
</form>
|
||||
|
||||
[% END %]
|
||||
|
||||
<hr>
|
||||
[% END %]
|
||||
|
||||
[% PROCESS global/footer.html.tmpl %]
|
||||
|
@ -42,20 +42,21 @@
|
||||
<tr>
|
||||
<td colspan="2"><hr></td>
|
||||
</tr>
|
||||
[% IF user.authorizer.can_change_password %]
|
||||
<tr>
|
||||
<th align="right">New password:</th>
|
||||
<td>
|
||||
<input type="password" name="new_password1">
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th align="right">New password:</th>
|
||||
<td>
|
||||
<input type="password" name="new_password1">
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th align="right">Re-enter new password:</th>
|
||||
<td>
|
||||
<input type="password" name="new_password2">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th align="right">Re-enter new password:</th>
|
||||
<td>
|
||||
<input type="password" name="new_password2">
|
||||
</td>
|
||||
</tr>
|
||||
[% END %]
|
||||
|
||||
<tr>
|
||||
<th align="right">Your real name (optional, but encouraged):</th>
|
||||
@ -64,7 +65,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
[% IF Param('allowemailchange') %]
|
||||
[% IF user.authorizer.can_change_email && Param('allowemailchange') %]
|
||||
[% IF login_change_date %]
|
||||
[% IF new_login_name %]
|
||||
<tr>
|
||||
|
@ -76,7 +76,7 @@
|
||||
are impersonating them.
|
||||
</p>
|
||||
|
||||
[% IF user.get_flag("can_logout") %]
|
||||
[% IF user.authorizer.can_login %]
|
||||
<p>
|
||||
Finally, enter your [% terms.Bugzilla %] password:
|
||||
<input type="hidden" name="Bugzilla_login" value="
|
||||
|
@ -48,15 +48,23 @@
|
||||
Attachment #[% attach_id FILTER html %] ([% description FILTER html %])
|
||||
is already obsolete.
|
||||
|
||||
[% ELSIF error == "auth_err" %]
|
||||
[% title = "Internal Authentication Error" %]
|
||||
[%# Authentication errors are in a template depending on the auth method,
|
||||
for pluggability.
|
||||
#%]
|
||||
[% INCLUDE "account/auth/$authmethod-error.html.tmpl" %]
|
||||
[% ELSIF error == "auth_invalid_email" %]
|
||||
[% title = "Invalid Email Address" %]
|
||||
We received an email address (<b>[% addr FILTER html %]</b>)
|
||||
that didn't pass our syntax checking for a legal email address,
|
||||
when trying to create or update your account.
|
||||
[% IF default %]
|
||||
A legal address must contain exactly one '@',
|
||||
and at least one '.' after the @.
|
||||
[% ELSE %]
|
||||
[%+ Param('emailregexpdesc') %]
|
||||
[% END %]
|
||||
It must also not contain any of these special characters:
|
||||
<tt>\ ( ) & < > , ; : " [ ]</tt>, or any whitespace.</tt>
|
||||
|
||||
[% ELSIF error == "authres_unhandled" %]
|
||||
An authorization handler return value was not handled by the login code.
|
||||
The result value of [% value FILTER html %] was not handled by
|
||||
the login code.
|
||||
|
||||
[% ELSIF error == "bad_page_cgi_id" %]
|
||||
[% title = "Invalid Page ID" %]
|
||||
@ -103,10 +111,27 @@
|
||||
[% ELSIF error == "cookies_need_value" %]
|
||||
Every cookie must have a value.
|
||||
|
||||
[% ELSIF error == "env_no_email" %]
|
||||
[% terms.Bugzilla %] did not receive an email address from the
|
||||
environment.
|
||||
[% IF Param("auth_env_email") %]
|
||||
This means that the '[% Param("auth_env_email") FILTER html %]'
|
||||
environment variable was empty or did not exist.
|
||||
[% ELSE %]
|
||||
You need to set the "auth_env_email" environment variable to
|
||||
the name of the environment variable that will contain the
|
||||
user's email address.
|
||||
[% END %]
|
||||
|
||||
[% ELSIF error == "extension_invalid" %]
|
||||
An error occured processing hook [% name FILTER html %] in
|
||||
extension [% extension FILTER html %].
|
||||
|
||||
[% ELSIF error == "extern_id_conflict" %]
|
||||
The external ID '[% extern_id FILTER html %]' already exists
|
||||
in the database for '[% username FILTER html %]', but your
|
||||
account source says that '[% extern_user FILTER html %]' has that ID.
|
||||
|
||||
[% ELSIF error == "field_type_mismatch" %]
|
||||
Cannot seem to handle <code>[% field FILTER html %]</code>
|
||||
and <code>[% type FILTER html %]</code> together.
|
||||
@ -247,6 +272,19 @@
|
||||
given.
|
||||
[% END %]
|
||||
|
||||
[% ELSIF error == "ldap_bind_failed" %]
|
||||
Failed to bind to the LDAP server. The error message was:
|
||||
<code>[% errstr FILTER html %]</code>
|
||||
|
||||
[% ELSIF error == "ldap_cannot_retreive_attr" %]
|
||||
The specified LDAP attribute [% attr FILTER html %] was not found.
|
||||
|
||||
[% ELSIF error == "ldap_connect_failed" %]
|
||||
Could not connect to the LDAP server <code>[% server FILTER html %]</code>.
|
||||
|
||||
[% ELSIF error == "ldap_server_not_defined" %]
|
||||
The LDAP server for authentication has not been defined.
|
||||
|
||||
[% ELSIF error == "missing_bug_id" %]
|
||||
No [% terms.bug %] ID was given.
|
||||
|
||||
|
@ -57,7 +57,7 @@
|
||||
[% IF user.login %]
|
||||
[% ' | <a href="sanitycheck.cgi">Sanity check</a>'
|
||||
IF user.groups.tweakparams %]
|
||||
[% IF user.get_flag('can_logout') %]
|
||||
[% IF user.authorizer.can_logout %]
|
||||
| <a href="relogin.cgi">Log out</a>
|
||||
[% ELSE %]
|
||||
| Logged in as
|
||||
@ -70,10 +70,13 @@
|
||||
[% user.login FILTER html %]
|
||||
[% END %]
|
||||
[% ELSE %]
|
||||
[% IF Param('createemailregexp') %]
|
||||
[% IF Param('createemailregexp')
|
||||
&& user.authorizer.user_can_create_account %]
|
||||
| <a href="createaccount.cgi">New Account</a>
|
||||
[% END %]
|
||||
| <a href="index.cgi?GoAheadAndLogIn=1">Log In</a>
|
||||
[% IF user.authorizer.can_login %]
|
||||
| <a href="index.cgi?GoAheadAndLogIn=1">Log In</a>
|
||||
[% END %]
|
||||
[% END %]
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1339,10 +1339,6 @@
|
||||
[% title = "Wrong Token" %]
|
||||
That token cannot be used to change your password.
|
||||
|
||||
[% ELSIF error == "extern_id_conflict" %]
|
||||
[% title = "Extern ID Conflict" %]
|
||||
Someone with a different external ID has that address.
|
||||
|
||||
[% ELSIF error == "wrong_token_for_confirming_email_change" %]
|
||||
[% title = "Wrong Token" %]
|
||||
That token cannot be used to change your email address.
|
||||
|
@ -69,12 +69,12 @@ function addSidebar() {
|
||||
<li id="report"><a href="report.cgi">Summary reports and charts</a></li>
|
||||
[% IF user.id %]
|
||||
<li id="userprefs"><a href="userprefs.cgi">Change password or user preferences</a></li>
|
||||
[% IF user.get_flag('can_logout') %]
|
||||
[% IF user.authorizer.can_logout %]
|
||||
<li id="logout"><a href="relogin.cgi">Log out [% user.login FILTER html %]</a></li>
|
||||
[% END %]
|
||||
[% ELSE %]
|
||||
[% ELSIF user.authorizer.can_login %]
|
||||
[% PROCESS "account/auth/login-small.html.tmpl" %]
|
||||
[% IF Param('createemailregexp') %]
|
||||
[% IF Param('createemailregexp') && user.authorizer.user_can_create_account %]
|
||||
<li id="account"><a href="createaccount.cgi">Open a new [% terms.Bugzilla %] account</a></li>
|
||||
[% END %]
|
||||
[% END %]
|
||||
|
@ -97,7 +97,7 @@ function normal_keypress_handler( aEvent ) {
|
||||
[%- IF user.groups.tweakparams %]
|
||||
<text class="text-link" onclick="load_relative_url('sanitycheck.cgi')" value="sanity check"/>
|
||||
[%- END %]
|
||||
[%- IF user.get_flag('can_logout') %]
|
||||
[%- IF user.authorizer.can_logout %]
|
||||
<text class="text-link" onclick="load_relative_url('relogin.cgi')" value="log out [% user.login FILTER html %]"/>
|
||||
[%- END %]
|
||||
<separator class="thin"/>
|
||||
|
@ -104,7 +104,7 @@ if ( $::action eq 'reqpw' ) {
|
||||
|| ThrowUserError("login_needed_for_password_change");
|
||||
|
||||
# check verification methods
|
||||
unless (Bugzilla::Auth->has_db) {
|
||||
unless (Bugzilla->user->authorizer->can_change_password) {
|
||||
ThrowUserError("password_change_requests_not_allowed");
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,8 @@ sub DoAccount {
|
||||
($vars->{'realname'}) = $dbh->selectrow_array(
|
||||
"SELECT realname FROM profiles WHERE userid = ?", undef, $user->id);
|
||||
|
||||
if(Param('allowemailchange')) {
|
||||
if(Param('allowemailchange')
|
||||
&& Bugzilla->user->authorizer->can_change_email) {
|
||||
my @token = $dbh->selectrow_array(
|
||||
"SELECT tokentype, issuedate + " .
|
||||
$dbh->sql_interval(3, 'DAY') . ", eventdata
|
||||
|
Loading…
x
Reference in New Issue
Block a user