CGI.pm 8.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
# -*- 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::CGI;

use strict;

use Bugzilla::Config;
use Bugzilla::Constants;
35
use Bugzilla::Error;
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
use Bugzilla::Util;

sub login {
    my ($class, $type) = @_;

    # 'NORMAL' logins depend on the 'requirelogin' param
    if ($type == LOGIN_NORMAL) {
        $type = Param('requirelogin') ? LOGIN_REQUIRED : LOGIN_OPTIONAL;
    }

    my $cgi = Bugzilla->cgi;

    # First, try the actual login method against form variables
    my $username = $cgi->param("Bugzilla_login");
    my $passwd = $cgi->param("Bugzilla_password");

    my $authmethod = Param("loginmethod");
    my ($authres, $userid, $extra, $info) =
      Bugzilla::Auth->authenticate($username, $passwd);

    if ($authres == AUTH_OK) {
        # Login via username/password was correct and valid, so create
        # and send out the login cookies
        my $ipaddr = $cgi->remote_addr;
        unless ($cgi->param('Bugzilla_restrictlogin') ||
                Param('loginnetmask') == 32) {
62
            $ipaddr = Bugzilla::Auth::get_netaddr($ipaddr);
63 64 65 66 67 68 69 70 71 72 73
        }

        # The IP address is valid, at least for comparing with itself in a
        # subsequent login
        trick_taint($ipaddr);

        my $dbh = Bugzilla->dbh;
        $dbh->do("INSERT INTO logincookies (userid, ipaddr) VALUES (?, ?)",
                 undef,
                 $userid, $ipaddr);
        my $logincookie = $dbh->selectrow_array("SELECT LAST_INSERT_ID()");
74

75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
        # Remember cookie only if admin has told so
        # or admin didn't forbid it and user told to remember.
        if ((Param('rememberlogin') eq 'on') ||
            ((Param('rememberlogin') ne 'off') &&
             ($cgi->param('Bugzilla_remember') eq 'on'))) {
            $cgi->send_cookie(-name => 'Bugzilla_login',
                              -value => $userid,
                              -expires => 'Fri, 01-Jan-2038 00:00:00 GMT');
            $cgi->send_cookie(-name => 'Bugzilla_logincookie',
                              -value => $logincookie,
                              -expires => 'Fri, 01-Jan-2038 00:00:00 GMT');

        }
        else {
            $cgi->send_cookie(-name => 'Bugzilla_login',
                              -value => $userid);
            $cgi->send_cookie(-name => 'Bugzilla_logincookie',
                              -value => $logincookie);

        }
95 96 97 98

        # compat code. The cookie value is used for logouts, and that
        # isn't generic yet.
        $::COOKIE{'Bugzilla_logincookie'} = $logincookie;
99 100
    }
    elsif ($authres == AUTH_NODATA) {
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
        # No data from the form, so try to login via cookies
        $username = $cgi->cookie("Bugzilla_login");
        $passwd = $cgi->cookie("Bugzilla_logincookie");

        require Bugzilla::Auth::Cookie;
        my $authmethod = "Cookie";

        ($authres, $userid, $extra) =
          Bugzilla::Auth::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) {
121 122 123 124 125 126
        ThrowCodeError("auth_err",
                       { authmethod => lc($authmethod),
                         userid => $userid,
                         auth_err_tag => $extra,
                         info => $info
                       });
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
    }

    # We can load the page if the login was ok, or there was no data
    # but a login wasn't required
    if ($authres == AUTH_OK ||
        ($authres == AUTH_NODATA && $type == LOGIN_OPTIONAL)) {

        # login succeded, so we're done
        return $userid;
    }

    # No login details were given, but we require a login if the
    # page does
    if ($authres == AUTH_NODATA && $type == LOGIN_REQUIRED) {
        # Throw up the login page

143
        print Bugzilla->cgi->header();
144 145 146 147 148 149 150 151 152

        my $template = Bugzilla->template;
        $template->process("account/auth/login.html.tmpl",
                           { 'target' => $cgi->url(-relative=>1),
                             'form' => \%::FORM,
                             'mform' => \%::MFORM,
                             'caneditaccount' => Bugzilla::Auth->can_edit,
                           }
                          )
153
          || ThrowTemplateError($template->error());
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168

        # This seems like as good as time as any to get rid of old
        # crufty junk in the logincookies table.  Get rid of any entry
        # that hasn't been used in a month.
        Bugzilla->dbh->do("DELETE FROM logincookies " .
                          "WHERE TO_DAYS(NOW()) - TO_DAYS(lastused) > 30");

        exit;
    }

    # The username/password may be wrong
    # Don't let the user know whether the username exists or whether
    # the password was just wrong. (This makes it harder for a cracker
    # to find account names by brute force)
    if ($authres == AUTH_LOGINFAILED) {
169
        ThrowUserError("invalid_username_or_password");
170 171 172 173 174
    }

    # The account may be disabled
    if ($authres == AUTH_DISABLED) {
        # Clear the cookie
175 176 177 178 179 180

        $cgi->send_cookie(-name => 'Bugzilla_login',
                          -expires => "Tue, 15-Sep-1998 21:49:00 GMT");
        $cgi->send_cookie(-name => 'Bugzilla_logincookie',
                          -expires => "Tue, 15-Sep-1998 21:49:00 GMT");

181
        # and throw a user error
182 183
        ThrowUserError("account_disabled",
                       {'disabled_reason' => $extra});
184 185 186
    }

    # If we get here, then we've run out of options, which shouldn't happen
187 188 189 190 191
    ThrowCodeError("authres_unhandled",
                   { authres => $authres,
                     type => $type,
                   }
                  );
192 193 194

}

195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
sub logout {
    my ($class, $user) = @_;

    if ($user) {
        # 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 simulataniously, 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
        my $dbh = Bugzilla->dbh;
        $dbh->do("DELETE FROM logincookies WHERE cookie = ? AND userid = ?",
                 undef, $::COOKIE{"Bugzilla_logincookie"}, $user->id);
    }

    my $cgi = Bugzilla->cgi;
    $cgi->send_cookie(-name => "Bugzilla_login",
                      -expires => "Tue, 15-Sep-1998 21:49:00 GMT");
    $cgi->send_cookie(-name => "Bugzilla_logincookie",
                      -expires => "Tue, 15-Sep-1998 21:49:00 GMT");
}

217 218 219 220 221 222 223 224 225 226 227
1;

__END__

=head1 NAME

Bugzilla::Auth::CGI - CGI-based logins for Bugzilla

=head1 SUMMARY

This is a L<login module|Bugzilla::Auth/"LOGIN"> for Bugzilla. Users connecting
228
from a CGI script use this module to authenticate. Logouts are also handled here.
229 230 231 232 233 234 235 236 237

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

238 239 240
When a logout is performed, we take care of removing the relevant
logincookie database entry and effectively deleting the client cookie.

241 242 243
=head1 SEE ALSO

L<Bugzilla::Auth>