Auth.pm 10.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
# -*- 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): Bradley Baetz <bbaetz@acm.org>
21
#                 Erik Stambaugh <erik@dasbistro.com>
22 23 24 25 26 27 28 29

package Bugzilla::Auth;

use strict;

use Bugzilla::Config;
use Bugzilla::Constants;

30 31 32 33
# The verification method that was successfully used upon login, if any
my $current_verify_class = undef;

# 'inherit' from the main verify method
34
BEGIN {
35 36 37 38 39 40 41
    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";
42
    }
43 44 45 46
}

# PRIVATE

47 48 49 50 51 52 53 54 55 56 57
# A number of features, like password change requests, require the DB
# verification method to be on the list.
sub has_db {
    for (split (/[\s,]+/, Param("user_verify_class"))) {
        if (/^DB$/) {
            return 1;
        }
    }
    return 0;
}

58
# Returns the network address for a given IP
59 60 61 62 63 64 65 66 67 68 69 70
sub get_netaddr {
    my $ipaddr = shift;

    # Check for a valid IPv4 addr which we know how to parse
    if (!$ipaddr || $ipaddr !~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/) {
        return undef;
    }

    my $addr = unpack("N", pack("CCCC", split(/\./, $ipaddr)));

    my $maskbits = Param('loginnetmask');

71 72 73
    # Make Bugzilla ignore the IP address if loginnetmask is set to 0
    return "0.0.0.0" if ($maskbits == 0);

74 75 76 77 78
    $addr >>= (32-$maskbits);
    $addr <<= (32-$maskbits);
    return join(".", unpack("CCCC", pack("N", $addr)));
}

79 80 81 82 83 84 85
# 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 = ();
86
    my $current_verify_method;
87
    for my $method (split /,\s*/, Param("user_verify_class")) {
88
        $current_verify_method = $method;
89 90 91 92 93
        $method = "Bugzilla::Auth::Verify::" . $method;
        @result = $method->authenticate(@args);
        @firstresult = @result unless @firstresult;

        if (($result[0] != AUTH_NODATA)&&($result[0] != AUTH_LOGINFAILED)) {
94
            unshift @result, ($current_verify_method);
95 96 97 98 99 100 101 102 103
            return @result;
        }
    }
    @result = @firstresult;
    # no auth match

    # see if we can set $current to the first verify method that
    # will allow a new login

104
    my $chosen_verify_method;
105
    for my $method (split /,\s*/, Param("user_verify_class")) {
106
        $current_verify_method = $method;
107 108
        $method = "Bugzilla::Auth::Verify::" . $method;
        if ($method->can_edit('new')) {
109
            $chosen_verify_method = $method;
110 111 112
        }
    }

113
    unshift @result, $chosen_verify_method;
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
    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;
}

131 132 133 134 135 136 137 138 139 140 141 142
1;

__END__

=head1 NAME

Bugzilla::Auth - Authentication handling for Bugzilla users

=head1 DESCRIPTION

Handles authentication for Bugzilla users.

143 144 145 146
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).
147

148 149
Modules for obtaining the data are located under L<Bugzilla::Auth::Login>, and
modules for authenticating are located in L<Bugzilla::Auth::Verify>.
150 151 152 153 154 155 156 157 158 159 160

=head1 METHODS

C<Bugzilla::Auth> contains several helper methods to be used by
authentication or login modules.

=over 4

=item C<Bugzilla::Auth::get_netaddr($ipaddr)>

Given an ip address, this returns the associated network address, using
161 162 163
C<Param('loginnetmask')> as the netmask. This can be used to obtain data
in order to restrict weak authentication methods (such as cookies) to
only some addresses.
164 165 166 167 168

=back

=head1 AUTHENTICATION

169
Authentication modules check a user's credentials (username, password,
170 171 172
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.
173 174 175 176 177 178 179

=head2 METHODS

=over 4

=item C<authenticate($username, $pass)>

180 181 182
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.
183

184 185 186 187 188
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.
189 190 191

=item C<AUTH_OK>

192
Authentication succeeded. The third variable is the userid of the new
193
user.
194 195 196 197 198 199 200 201

=item 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.

=item C<AUTH_ERROR>

202
An error occurred when trying to use the login mechanism. The third return
203
value may contain the Bugzilla userid, but will probably be C<undef>,
204
signifiying that the userid is unknown. The fourth value is a tag describing
205
the error used by the authentication error templates to print a description
206
to the user. The optional fifth argument is a hashref of values used as part
207 208 209 210 211 212 213 214 215
of the tag's error descriptions.

This error template must have a name/location of
I<account/auth/C<lc(authentication-type)>-error.html.tmpl>.

=item C<AUTH_LOGINFAILED>

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
216
username, the third argument may be the userid. The authentication
217 218 219 220 221
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.

222
The fourth argument is an optional tag from the authentication server
223 224
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
225
present as a fifth argument, to be used by the tag to give more detailed 
226 227 228 229
information.

=item C<AUTH_DISABLED>

230
The user successfully logged in, but their account has been disabled.
231
The third argument in the returned array is the userid, and the fourth
232 233 234
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.
235

236 237 238 239 240 241 242 243
=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.

244 245
=item C<can_edit>

246 247 248 249 250 251 252 253 254 255 256 257
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.
258 259 260 261 262

=back

=head1 LOGINS

263
A login module can be used to try to log in a Bugzilla user in a
264 265
particular way. For example,
L<Bugzilla::Auth::Login::WWW::CGI|Bugzilla::Auth::Login::WWW::CGI>
266 267
logs in users from CGI scripts, first by using form variables, and then
by trying cookies as a fallback.
268

269 270
The login interface consists of the following methods:

271 272
=over 4

273 274 275 276 277 278 279 280 281 282 283
=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:
284 285 286

=item C<LOGIN_OPTIONAL>

287 288 289 290
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.
291 292 293 294 295 296 297 298 299 300

=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.

301 302 303 304 305
=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>
306

307 308 309 310 311 312 313 314 315 316 317 318 319 320
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
321 322 323

=head1 SEE ALSO

324
L<Bugzilla::Auth::Login::WWW::CGI>, L<Bugzilla::Auth::Login::WWW::CGI::Cookie>, L<Bugzilla::Auth::Verify::DB>
325